diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/input.js b/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/input.js index e1a7074425..ae3f4b33c0 100644 --- a/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/input.js +++ b/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/input.js @@ -20,6 +20,16 @@ class Foo { static get foo() {} static set foo(bar) {} static static() {} + static * foo() {} + static async * foo() {} + + static #foo() {} + static async #foo() {} + static ["foo"]() {} + static get #foo() {} + static set #foo(taz) {} + static * #foo() {} + static async * #foo() {} get () {} diff --git a/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/output.js b/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/output.js index fbb5a3097e..24f3e9229c 100644 --- a/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/output.js +++ b/packages/babel-generator/test/fixtures/types/ClassBody-MethodDefinition/output.js @@ -37,6 +37,24 @@ class Foo { static static() {} + static *foo() {} + + static async *foo() {} + + static #foo() {} + + static async #foo() {} + + static ["foo"]() {} + + static get #foo() {} + + static set #foo(taz) {} + + static *#foo() {} + + static async *#foo() {} + get() {} set() {} diff --git a/packages/babel-helper-create-class-features-plugin/src/features.js b/packages/babel-helper-create-class-features-plugin/src/features.js index 5ad2cf2694..fffc0d0014 100644 --- a/packages/babel-helper-create-class-features-plugin/src/features.js +++ b/packages/babel-helper-create-class-features-plugin/src/features.js @@ -67,9 +67,9 @@ export function verifyUsedFeatures(path, file) { throw path.buildCodeFrameError("Class private methods are not enabled."); } - if (path.node.static) { + if (path.node.static && path.node.kind !== "method") { throw path.buildCodeFrameError( - "@babel/plugin-class-features doesn't support class static private methods yet.", + "@babel/plugin-class-features doesn't support class static private accessors yet.", ); } } diff --git a/packages/babel-helper-create-class-features-plugin/src/fields.js b/packages/babel-helper-create-class-features-plugin/src/fields.js index de121f9cf5..e29ff33776 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.js +++ b/packages/babel-helper-create-class-features-plugin/src/fields.js @@ -24,7 +24,7 @@ export function buildPrivateNamesMap(props) { update.getId = prop.scope.generateUidIdentifier(`get_${name}`); } else if (prop.node.kind === "set") { update.setId = prop.scope.generateUidIdentifier(`set_${name}`); - } else if (prop.node.kind === "method" && isMethod && isInstance) { + } else if (prop.node.kind === "method") { update.methodId = prop.scope.generateUidIdentifier(name); } privateNamesMap.set(name, update); @@ -141,12 +141,18 @@ const privateNameHandlerSpec = { setId, } = privateNamesMap.get(name); - if (isStatic && !isMethod) { - return t.callExpression( - file.addHelper("classStaticPrivateFieldSpecGet"), - [this.receiver(member), t.cloneNode(classRef), t.cloneNode(id)], - ); + if (isStatic) { + const helperName = isMethod + ? "classStaticPrivateMethodGet" + : "classStaticPrivateFieldSpecGet"; + + return t.callExpression(file.addHelper(helperName), [ + this.receiver(member), + t.cloneNode(classRef), + t.cloneNode(id), + ]); } + if (isMethod) { if (getId || setId) { return t.callExpression(file.addHelper("classPrivateFieldGet"), [ @@ -176,11 +182,17 @@ const privateNameHandlerSpec = { setId, } = privateNamesMap.get(name); - if (isStatic && !isMethod) { - return t.callExpression( - file.addHelper("classStaticPrivateFieldSpecSet"), - [this.receiver(member), t.cloneNode(classRef), t.cloneNode(id), value], - ); + if (isStatic) { + const helperName = isMethod + ? "classStaticPrivateMethodSet" + : "classStaticPrivateFieldSpecSet"; + + return t.callExpression(file.addHelper(helperName), [ + this.receiver(member), + t.cloneNode(classRef), + t.cloneNode(id), + value, + ]); } if (isMethod) { if (setId) { @@ -403,14 +415,28 @@ function buildPublicFieldInitSpec(ref, prop, state) { ); } -function buildPrivateInstanceMethodDeclaration(prop, privateNamesMap) { +function buildPrivateStaticMethodInitLoose(ref, prop, state, privateNamesMap) { + const { id, methodId } = privateNamesMap.get(prop.node.key.id.name); + return template.statement.ast` + Object.defineProperty(${ref}, ${id}, { + // configurable is false by default + // enumerable is false by default + // writable is false by default + value: ${methodId.name} + }); + `; +} + +function buildPrivateMethodDeclaration(prop, privateNamesMap, loose = false) { const privateName = privateNamesMap.get(prop.node.key.id.name); const { + id, methodId, getId, setId, getterDeclared, setterDeclared, + static: isStatic, } = privateName; const { params, body, generator, async } = prop.node; const methodValue = t.functionExpression( @@ -441,6 +467,14 @@ function buildPrivateInstanceMethodDeclaration(prop, privateNamesMap) { t.variableDeclarator(setId, methodValue), ]); } + if (isStatic && !loose) { + return t.variableDeclaration("var", [ + t.variableDeclarator( + id, + t.functionExpression(id, params, body, generator, async), + ), + ]); + } return t.variableDeclaration("var", [ t.variableDeclarator(methodId, methodValue), @@ -472,8 +506,9 @@ function replaceThisContext(path, ref, superRef, file, loose) { }); replacer.isStatic = true; replacer.replace(); - - path.traverse(thisContextVisitor, state); + if (path.isProperty()) { + path.traverse(thisContextVisitor, state); + } return state.needsClassRef; } @@ -497,7 +532,7 @@ export function buildFieldsInitNodes( const isField = prop.isProperty(); const isMethod = !isField; - if (isStatic && isField) { + if (isStatic) { const replaced = replaceThisContext(prop, ref, superRef, state, loose); needsClassRef = needsClassRef || replaced; } @@ -548,7 +583,7 @@ export function buildFieldsInitNodes( ), ); staticNodes.push( - buildPrivateInstanceMethodDeclaration(prop, privateNamesMap), + buildPrivateMethodDeclaration(prop, privateNamesMap, loose), ); break; case isInstance && isPrivate && isMethod && !loose: @@ -560,7 +595,27 @@ export function buildFieldsInitNodes( ), ); staticNodes.push( - buildPrivateInstanceMethodDeclaration(prop, privateNamesMap), + buildPrivateMethodDeclaration(prop, privateNamesMap, loose), + ); + break; + case isStatic && isPrivate && isMethod && !loose: + needsClassRef = true; + staticNodes.push( + buildPrivateMethodDeclaration(prop, privateNamesMap, loose), + ); + break; + case isStatic && isPrivate && isMethod && loose: + needsClassRef = true; + staticNodes.push( + buildPrivateMethodDeclaration(prop, privateNamesMap, loose), + ); + staticNodes.push( + buildPrivateStaticMethodInitLoose( + t.cloneNode(ref), + prop, + state, + privateNamesMap, + ), ); break; case isInstance && isPublic && isField && loose: diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 7d2cbf4f03..2448e0cdea 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -1102,7 +1102,21 @@ helpers.classStaticPrivateFieldSpecSet = helper("7.0.2")` descriptor.value = value; return value; } - +`; + +helpers.classStaticPrivateMethodGet = helper("7.3.2")` + export default function _classStaticPrivateMethodGet(receiver, classConstructor, method) { + if (receiver !== classConstructor) { + throw new TypeError("Private static access of wrong provenance"); + } + return method; + } +`; + +helpers.classStaticPrivateMethodSet = helper("7.3.2")` + export default function _classStaticPrivateMethodSet() { + throw new TypeError("attempted to set read only static private field"); + } `; helpers.decorate = helper("7.1.5")` diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/input.js new file mode 100644 index 0000000000..99125b6f24 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/input.js @@ -0,0 +1,13 @@ +class Cl { + static async #privateStaticMethod() { + return 2; + } + + test() { + return Cl.#privateStaticMethod(); + } +} + +return new Cl().test().then(val => { + expect(val).toBe(2); +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/options.json b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/options.json new file mode 100644 index 0000000000..a34201ad4f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/options.json @@ -0,0 +1,18 @@ +{ + "minNodeVersion": "8.0.0", + "plugins": [ + [ + "external-helpers", + { + "helperVersion": "7.1000.0" + } + ], + "proposal-private-methods", + "proposal-class-properties", + "transform-block-scoping", + "syntax-class-properties" + ], + "parserOpts": { + "allowReturnOutsideFunction": true + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/output.js new file mode 100644 index 0000000000..d21e2139e8 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/async/output.js @@ -0,0 +1,14 @@ +class Cl { + test() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + +} + +var _privateStaticMethod = async function _privateStaticMethod() { + return 2; +}; + +return new Cl().test().then(val => { + expect(val).toBe(2); +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/exec.js new file mode 100644 index 0000000000..d5577057ca --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/exec.js @@ -0,0 +1,33 @@ +const privateStaticValue = 1017; + +class Cl { + static staticMethod2() { + return Cl.#privateStaticMethod(); + } + + static #privateStaticMethod() { + return privateStaticValue; + } + + static staticMethod() { + return Cl.#privateStaticMethod(); + } + + static privateStaticMethod() { + return Cl.#privateStaticMethod(); + } + + publicMethod() { + return Cl.#privateStaticMethod(); + } + + constructor() { + this.instanceField = Cl.#privateStaticMethod(); + } +} + +expect((new Cl).publicMethod()).toEqual(privateStaticValue); +// expect((new Cl).instanceField).toEqual(privateStaticValue); +// expect(Cl.privateStaticMethod()).toEqual(privateStaticValue); +// expect(Cl.staticMethod()).toEqual(privateStaticValue); +// expect(Cl.staticMethod2()).toEqual(privateStaticValue); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/input.js new file mode 100644 index 0000000000..ad0ee3d448 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/input.js @@ -0,0 +1,25 @@ +class Cl { + static staticMethod2() { + return Cl.#privateStaticMethod(); + } + + static #privateStaticMethod() { + return 1017; + } + + static staticMethod() { + return Cl.#privateStaticMethod(); + } + + static privateStaticMethod() { + return Cl.#privateStaticMethod(); + } + + publicMethod() { + return Cl.#privateStaticMethod(); + } + + constructor() { + this.instanceField = Cl.#privateStaticMethod(); + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/output.js new file mode 100644 index 0000000000..a6fe9f8e28 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/basic/output.js @@ -0,0 +1,32 @@ +class Cl { + static staticMethod2() { + return babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod](); + } + + static staticMethod() { + return babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod](); + } + + static privateStaticMethod() { + return babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod](); + } + + publicMethod() { + return babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod](); + } + + constructor() { + this.instanceField = babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod](); + } + +} + +var _privateStaticMethod = babelHelpers.classPrivateFieldLooseKey("privateStaticMethod"); + +var _privateStaticMethod2 = function _privateStaticMethod2() { + return 1017; +}; + +Object.defineProperty(Cl, _privateStaticMethod, { + value: _privateStaticMethod2 +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/exec.js new file mode 100644 index 0000000000..940b62801e --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/exec.js @@ -0,0 +1,12 @@ +class Cl { + static #privateStaticMethod() { + return 1017; + } + + publicMethod(checked) { + return checked.#privateStaticMethod(); + } +} + +const cl = new Cl(); +expect(cl.publicMethod(Cl)).toBe(1017); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/input.js new file mode 100644 index 0000000000..fd8c6c331e --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/input.js @@ -0,0 +1,7 @@ +class Cl { + static #privateStaticMethod() { } + + publicMethod(checked) { + return checked.#privateStaticMethod(); + } +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/output.js new file mode 100644 index 0000000000..b93f1e9d25 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/class-check/output.js @@ -0,0 +1,14 @@ +class Cl { + publicMethod(checked) { + return babelHelpers.classPrivateFieldLooseBase(checked, _privateStaticMethod)[_privateStaticMethod](); + } + +} + +var _privateStaticMethod = babelHelpers.classPrivateFieldLooseKey("privateStaticMethod"); + +var _privateStaticMethod2 = function _privateStaticMethod2() {}; + +Object.defineProperty(Cl, _privateStaticMethod, { + value: _privateStaticMethod2 +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/exec.js new file mode 100644 index 0000000000..14a7b5edd8 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/exec.js @@ -0,0 +1,20 @@ +let exfiltrated; + +class Cl { + static #privateStaticMethod() { + return 1017; + } + + constructor() { + if (exfiltrated === undefined) { + exfiltrated = Cl.#privateStaticMethod; + } + expect(exfiltrated).toStrictEqual(Cl.#privateStaticMethod); + } +} + +new Cl(); +// check for private method function object equality +new Cl(); + +expect(exfiltrated()).toEqual(1017); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/input.js new file mode 100644 index 0000000000..fdc78e5798 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/input.js @@ -0,0 +1,13 @@ +let exfiltrated; + +class Cl { + static #privateStaticMethod() { + return 1017; + } + + constructor() { + if (exfiltrated === undefined) { + exfiltrated = Cl.#privateStaticMethod; + } + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/output.js new file mode 100644 index 0000000000..ebdf256588 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/exfiltrated/output.js @@ -0,0 +1,20 @@ +var exfiltrated; + +class Cl { + constructor() { + if (exfiltrated === undefined) { + exfiltrated = babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod]; + } + } + +} + +var _privateStaticMethod = babelHelpers.classPrivateFieldLooseKey("privateStaticMethod"); + +var _privateStaticMethod2 = function _privateStaticMethod2() { + return 1017; +}; + +Object.defineProperty(Cl, _privateStaticMethod, { + value: _privateStaticMethod2 +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/exec.js new file mode 100644 index 0000000000..97a6a708fc --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/exec.js @@ -0,0 +1,14 @@ +class Cl { + static *#foo() { + yield 2; + return 3; + } + + test() { + return Cl.#foo(); + } +} + +const val = new Cl().test(); +expect(val.next()).toEqual({ value: 2, done: false }); +expect(val.next()).toEqual({ value: 3, done: true }); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/input.js new file mode 100644 index 0000000000..2b8727d64c --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/input.js @@ -0,0 +1,10 @@ +class Cl { + static *#foo() { + yield 2; + return 3; + } + + test() { + return Cl.#foo(); + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/output.js new file mode 100644 index 0000000000..2f1d025947 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/generator/output.js @@ -0,0 +1,17 @@ +class Cl { + test() { + return babelHelpers.classPrivateFieldLooseBase(Cl, _foo)[_foo](); + } + +} + +var _foo = babelHelpers.classPrivateFieldLooseKey("foo"); + +var _foo2 = function* _foo2() { + yield 2; + return 3; +}; + +Object.defineProperty(Cl, _foo, { + value: _foo2 +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/options.json b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/options.json new file mode 100644 index 0000000000..50c7612f83 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/options.json @@ -0,0 +1,14 @@ +{ + "plugins": [ + [ + "external-helpers", + { + "helperVersion": "7.1000.0" + } + ], + ["proposal-private-methods", { "loose": true }], + ["proposal-class-properties", { "loose": true }], + "transform-block-scoping", + "syntax-class-properties" + ] +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/exec.js new file mode 100644 index 0000000000..d55af8ba71 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/exec.js @@ -0,0 +1,9 @@ +class Cl { + static #privateStaticMethod() { } + + constructor() { + expect(() => Cl.#privateStaticMethod = null).toThrow(TypeError); + } +} + +new Cl(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/input.js new file mode 100644 index 0000000000..865ddea4cc --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/input.js @@ -0,0 +1,9 @@ +class Cl { + static #privateStaticMethod() { } + + constructor() { + Cl.#privateStaticMethod = null; + } +} + +new Cl(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/output.js new file mode 100644 index 0000000000..1be439447f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/reassignment/output.js @@ -0,0 +1,15 @@ +class Cl { + constructor() { + babelHelpers.classPrivateFieldLooseBase(Cl, _privateStaticMethod)[_privateStaticMethod] = null; + } + +} + +var _privateStaticMethod = babelHelpers.classPrivateFieldLooseKey("privateStaticMethod"); + +var _privateStaticMethod2 = function _privateStaticMethod2() {}; + +Object.defineProperty(Cl, _privateStaticMethod, { + value: _privateStaticMethod2 +}); +new Cl(); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/scopable/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/scopable/exec.js new file mode 100644 index 0000000000..f94e5c495f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/scopable/exec.js @@ -0,0 +1,17 @@ +class Cl { + static #privateMethodA() { + const i = 40; + return i; + } + + static #privateMethodB() { + const i = 2; + return i; + } + + publicMethod() { + return Cl.#privateMethodA() + Cl.#privateMethodB(); + } +} + +expect((new Cl).publicMethod()).toEqual(42); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/exec.js new file mode 100644 index 0000000000..2793aa6e60 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/exec.js @@ -0,0 +1,15 @@ +class Base { + static basePublicStaticMethod() { return 1017; } +} + +class Sub extends Base { + static #subStaticPrivateMethod() { + return super.basePublicStaticMethod(); + } + + static check() { + return Sub.#subStaticPrivateMethod(); + } +} + +expect(Sub.check()).toEqual(1017); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/input.js new file mode 100644 index 0000000000..2a8aba080f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/input.js @@ -0,0 +1,13 @@ +class Base { + static basePublicStaticMethod() { return 1017; } +} + +class Sub extends Base { + static #subStaticPrivateMethod() { + return super.basePublicStaticMethod(); + } + + static check() { + Sub.#subStaticPrivateMethod(); + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/output.js new file mode 100644 index 0000000000..4f6bc36984 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/super/output.js @@ -0,0 +1,23 @@ +class Base { + static basePublicStaticMethod() { + return 1017; + } + +} + +class Sub extends Base { + static check() { + babelHelpers.classPrivateFieldLooseBase(Sub, _subStaticPrivateMethod)[_subStaticPrivateMethod](); + } + +} + +var _subStaticPrivateMethod = babelHelpers.classPrivateFieldLooseKey("subStaticPrivateMethod"); + +var _subStaticPrivateMethod2 = function _subStaticPrivateMethod2() { + return Base.basePublicStaticMethod.call(this); +}; + +Object.defineProperty(Sub, _subStaticPrivateMethod, { + value: _subStaticPrivateMethod2 +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/exec.js new file mode 100644 index 0000000000..3262c36845 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/exec.js @@ -0,0 +1,18 @@ +class A { + static get a() { return 1 } +} + +class B extends A { + static get b() { return 2 } + + static #getA() { return super.a } + static #getB() { return this.b } + + static extract() { + return [this.#getA, this.#getB]; + } +} + +const [getA, getB] = B.extract(); +expect(getA.call({ a: 3 })).toBe(1); +expect(getB.call({ b: 4 })).toBe(4); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/input.js new file mode 100644 index 0000000000..3c8639776d --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/input.js @@ -0,0 +1,16 @@ +class A { + static get a() { return 1 } +} + +class B extends A { + static get b() { return 2 } + + static #getA() { return super.a } + static #getB() { return this.b } + + static extract() { + return [this.#getA, this.#getB]; + } +} + +const [getA, getB] = B.extract(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/output.js new file mode 100644 index 0000000000..4a5cddf861 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method-loose/this/output.js @@ -0,0 +1,38 @@ +class A { + static get a() { + return 1; + } + +} + +class B extends A { + static get b() { + return 2; + } + + static extract() { + return [babelHelpers.classPrivateFieldLooseBase(this, _getA)[_getA], babelHelpers.classPrivateFieldLooseBase(this, _getB)[_getB]]; + } + +} + +var _getA = babelHelpers.classPrivateFieldLooseKey("getA"); + +var _getB = babelHelpers.classPrivateFieldLooseKey("getB"); + +var _getA2 = function _getA2() { + return A.a; +}; + +Object.defineProperty(B, _getA, { + value: _getA2 +}); + +var _getB2 = function _getB2() { + return this.b; +}; + +Object.defineProperty(B, _getB, { + value: _getB2 +}); +var [getA, getB] = B.extract(); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/input.js new file mode 100644 index 0000000000..99125b6f24 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/input.js @@ -0,0 +1,13 @@ +class Cl { + static async #privateStaticMethod() { + return 2; + } + + test() { + return Cl.#privateStaticMethod(); + } +} + +return new Cl().test().then(val => { + expect(val).toBe(2); +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/options.json b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/options.json new file mode 100644 index 0000000000..a34201ad4f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/options.json @@ -0,0 +1,18 @@ +{ + "minNodeVersion": "8.0.0", + "plugins": [ + [ + "external-helpers", + { + "helperVersion": "7.1000.0" + } + ], + "proposal-private-methods", + "proposal-class-properties", + "transform-block-scoping", + "syntax-class-properties" + ], + "parserOpts": { + "allowReturnOutsideFunction": true + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/output.js new file mode 100644 index 0000000000..d21e2139e8 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/async/output.js @@ -0,0 +1,14 @@ +class Cl { + test() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + +} + +var _privateStaticMethod = async function _privateStaticMethod() { + return 2; +}; + +return new Cl().test().then(val => { + expect(val).toBe(2); +}); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/exec.js new file mode 100644 index 0000000000..ca3487099e --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/exec.js @@ -0,0 +1,33 @@ +const privateStaticValue = 1017; + +class Cl { + static staticMethod2() { + return Cl.#privateStaticMethod(); + } + + static #privateStaticMethod() { + return privateStaticValue; + } + + static staticMethod() { + return Cl.#privateStaticMethod(); + } + + static privateStaticMethod() { + return Cl.#privateStaticMethod(); + } + + publicMethod() { + return Cl.#privateStaticMethod(); + } + + constructor() { + this.instanceField = Cl.#privateStaticMethod(); + } +} + +expect((new Cl).publicMethod()).toEqual(privateStaticValue); +expect((new Cl).instanceField).toEqual(privateStaticValue); +expect(Cl.privateStaticMethod()).toEqual(privateStaticValue); +expect(Cl.staticMethod()).toEqual(privateStaticValue); +expect(Cl.staticMethod2()).toEqual(privateStaticValue); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/input.js new file mode 100644 index 0000000000..ad0ee3d448 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/input.js @@ -0,0 +1,25 @@ +class Cl { + static staticMethod2() { + return Cl.#privateStaticMethod(); + } + + static #privateStaticMethod() { + return 1017; + } + + static staticMethod() { + return Cl.#privateStaticMethod(); + } + + static privateStaticMethod() { + return Cl.#privateStaticMethod(); + } + + publicMethod() { + return Cl.#privateStaticMethod(); + } + + constructor() { + this.instanceField = Cl.#privateStaticMethod(); + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/output.js new file mode 100644 index 0000000000..2c56c6dedc --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/basic/output.js @@ -0,0 +1,26 @@ +class Cl { + static staticMethod2() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + + static staticMethod() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + + static privateStaticMethod() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + + publicMethod() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + + constructor() { + this.instanceField = babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod).call(Cl); + } + +} + +var _privateStaticMethod = function _privateStaticMethod() { + return 1017; +}; diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/exec.js new file mode 100644 index 0000000000..940b62801e --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/exec.js @@ -0,0 +1,12 @@ +class Cl { + static #privateStaticMethod() { + return 1017; + } + + publicMethod(checked) { + return checked.#privateStaticMethod(); + } +} + +const cl = new Cl(); +expect(cl.publicMethod(Cl)).toBe(1017); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/input.js new file mode 100644 index 0000000000..fd8c6c331e --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/input.js @@ -0,0 +1,7 @@ +class Cl { + static #privateStaticMethod() { } + + publicMethod(checked) { + return checked.#privateStaticMethod(); + } +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/output.js new file mode 100644 index 0000000000..06b25f9a78 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/class-check/output.js @@ -0,0 +1,8 @@ +class Cl { + publicMethod(checked) { + return babelHelpers.classStaticPrivateMethodGet(checked, Cl, _privateStaticMethod).call(checked); + } + +} + +var _privateStaticMethod = function _privateStaticMethod() {}; diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/exec.js new file mode 100644 index 0000000000..14a7b5edd8 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/exec.js @@ -0,0 +1,20 @@ +let exfiltrated; + +class Cl { + static #privateStaticMethod() { + return 1017; + } + + constructor() { + if (exfiltrated === undefined) { + exfiltrated = Cl.#privateStaticMethod; + } + expect(exfiltrated).toStrictEqual(Cl.#privateStaticMethod); + } +} + +new Cl(); +// check for private method function object equality +new Cl(); + +expect(exfiltrated()).toEqual(1017); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/input.js new file mode 100644 index 0000000000..fdc78e5798 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/input.js @@ -0,0 +1,13 @@ +let exfiltrated; + +class Cl { + static #privateStaticMethod() { + return 1017; + } + + constructor() { + if (exfiltrated === undefined) { + exfiltrated = Cl.#privateStaticMethod; + } + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/output.js new file mode 100644 index 0000000000..6edcd3d51a --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/exfiltrated/output.js @@ -0,0 +1,14 @@ +var exfiltrated; + +class Cl { + constructor() { + if (exfiltrated === undefined) { + exfiltrated = babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _privateStaticMethod); + } + } + +} + +var _privateStaticMethod = function _privateStaticMethod() { + return 1017; +}; diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/exec.js new file mode 100644 index 0000000000..97a6a708fc --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/exec.js @@ -0,0 +1,14 @@ +class Cl { + static *#foo() { + yield 2; + return 3; + } + + test() { + return Cl.#foo(); + } +} + +const val = new Cl().test(); +expect(val.next()).toEqual({ value: 2, done: false }); +expect(val.next()).toEqual({ value: 3, done: true }); diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/input.js new file mode 100644 index 0000000000..2b8727d64c --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/input.js @@ -0,0 +1,10 @@ +class Cl { + static *#foo() { + yield 2; + return 3; + } + + test() { + return Cl.#foo(); + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/output.js new file mode 100644 index 0000000000..be6d0e6bf2 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/generator/output.js @@ -0,0 +1,11 @@ +class Cl { + test() { + return babelHelpers.classStaticPrivateMethodGet(Cl, Cl, _foo).call(Cl); + } + +} + +var _foo = function* _foo() { + yield 2; + return 3; +}; diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/options.json b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/options.json new file mode 100644 index 0000000000..c5e6649e4e --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/options.json @@ -0,0 +1,14 @@ +{ + "plugins": [ + [ + "external-helpers", + { + "helperVersion": "7.1000.0" + } + ], + "proposal-private-methods", + "proposal-class-properties", + "transform-block-scoping", + "syntax-class-properties" + ] +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/reassignment/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/reassignment/exec.js new file mode 100644 index 0000000000..d55af8ba71 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/reassignment/exec.js @@ -0,0 +1,9 @@ +class Cl { + static #privateStaticMethod() { } + + constructor() { + expect(() => Cl.#privateStaticMethod = null).toThrow(TypeError); + } +} + +new Cl(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/scopable/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/scopable/exec.js new file mode 100644 index 0000000000..f94e5c495f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/scopable/exec.js @@ -0,0 +1,17 @@ +class Cl { + static #privateMethodA() { + const i = 40; + return i; + } + + static #privateMethodB() { + const i = 2; + return i; + } + + publicMethod() { + return Cl.#privateMethodA() + Cl.#privateMethodB(); + } +} + +expect((new Cl).publicMethod()).toEqual(42); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/exec.js new file mode 100644 index 0000000000..2793aa6e60 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/exec.js @@ -0,0 +1,15 @@ +class Base { + static basePublicStaticMethod() { return 1017; } +} + +class Sub extends Base { + static #subStaticPrivateMethod() { + return super.basePublicStaticMethod(); + } + + static check() { + return Sub.#subStaticPrivateMethod(); + } +} + +expect(Sub.check()).toEqual(1017); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/input.js new file mode 100644 index 0000000000..2a8aba080f --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/input.js @@ -0,0 +1,13 @@ +class Base { + static basePublicStaticMethod() { return 1017; } +} + +class Sub extends Base { + static #subStaticPrivateMethod() { + return super.basePublicStaticMethod(); + } + + static check() { + Sub.#subStaticPrivateMethod(); + } +} diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/output.js new file mode 100644 index 0000000000..156343f956 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/super/output.js @@ -0,0 +1,17 @@ +class Base { + static basePublicStaticMethod() { + return 1017; + } + +} + +class Sub extends Base { + static check() { + babelHelpers.classStaticPrivateMethodGet(Sub, Sub, _subStaticPrivateMethod).call(Sub); + } + +} + +var _subStaticPrivateMethod = function _subStaticPrivateMethod() { + return babelHelpers.get(babelHelpers.getPrototypeOf(Sub), "basePublicStaticMethod", this).call(this); +}; diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/exec.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/exec.js new file mode 100644 index 0000000000..3262c36845 --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/exec.js @@ -0,0 +1,18 @@ +class A { + static get a() { return 1 } +} + +class B extends A { + static get b() { return 2 } + + static #getA() { return super.a } + static #getB() { return this.b } + + static extract() { + return [this.#getA, this.#getB]; + } +} + +const [getA, getB] = B.extract(); +expect(getA.call({ a: 3 })).toBe(1); +expect(getB.call({ b: 4 })).toBe(4); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/input.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/input.js new file mode 100644 index 0000000000..3c8639776d --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/input.js @@ -0,0 +1,16 @@ +class A { + static get a() { return 1 } +} + +class B extends A { + static get b() { return 2 } + + static #getA() { return super.a } + static #getB() { return this.b } + + static extract() { + return [this.#getA, this.#getB]; + } +} + +const [getA, getB] = B.extract(); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/output.js b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/output.js new file mode 100644 index 0000000000..e440955ecc --- /dev/null +++ b/packages/babel-plugin-proposal-private-methods/test/fixtures/private-static-method/this/output.js @@ -0,0 +1,27 @@ +class A { + static get a() { + return 1; + } + +} + +class B extends A { + static get b() { + return 2; + } + + static extract() { + return [babelHelpers.classStaticPrivateMethodGet(this, B, _getA), babelHelpers.classStaticPrivateMethodGet(this, B, _getB)]; + } + +} + +var _getA = function _getA() { + return babelHelpers.get(babelHelpers.getPrototypeOf(B), "a", this); +}; + +var _getB = function _getB() { + return this.b; +}; + +var [getA, getB] = B.extract();