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
Allow compiling #foo in obj
without compiling private fields
#13172
Allow compiling #foo in obj
without compiling private fields
#13172
Conversation
preset-env
's shippedProposals
(#13114)#foo in obj
without compiling private fields
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 7fcf7c4:
|
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/45465/ |
@@ -0,0 +1,14 @@ | |||
var _FooBrandCheck = new WeakSet(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also place FooBrandCheck to a private static field, e.g.
class Foo {
private static #_FooBrandCheck = new WeakSet();
constructor() {
Foo.#_FooBrandCheck.add(this);
}
get #foo() {}
test(other) {
return Foo.#_FooBrandCheck.has(other);
}
}
so we avoid the edge cases when FooBrandCheck
might be injected to an incorrect scope, e.g. when a class is in the param initializer.
// <-- FooBrandCheck will be injected to here
(x = class { #foo; test (other) { return #foo in other } }) => {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh I love this idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish class access were not stage-2. The class binding can be overwritten. Guess we may have to stick to temporary variable.
get #foo() {} | ||
|
||
test(other) { | ||
return _FooBrandCheck.has(other); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Is _FooBrandCheck.has(other)
equivalent to other instanceof Foo
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's different when other
is a subclass of Foo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If Bar
, a subclass of Foo
, has super()
in its constructor, does it imply #foo in bar
is true
where bar
is an instance of Bar
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I was confusing this test with the private fields one. instanceof
is different because it checks the prototype:
class Base {
constructor() {
return {};
}
}
class Foo extends Base {
#foo;
static check(obj) {
return #foo in obj;
}
}
new Foo() instanceof Foo; // false
Foo.check(new Foo); // true
class Foo {
#foo;
static check(obj) {
return #foo in obj;
}
}
let foo = new Foo();
foo.__proto__ = {};
foo instanceof Foo; // false
Foo.check(foo); // true
class Foo {
#foo;
static check(obj) {
return #foo in obj;
}
}
let foo = Object.create(Foo.prototype);
foo instanceof Foo; // true
Foo.check(foo); // false
If
Bar
, a subclass ofFoo
, hassuper()
in its constructor, does it imply#foo in bar
istrue
wherebar
is an instance ofBar
?
If by "bar
is an instance of Bar
" you mean bar instanceof Bar
then no, there isn't an implication in any direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're going to have ordering issues:
class Foo {
#x = 1;
y = #x in this;
}
assert.equal(new Foo().y, true);
0c58c2e
to
838211b
Compare
3d06565
to
6f7cfa8
Compare
Fixed both the bugs! |
packages/babel-plugin-proposal-private-property-in-object/src/native-private-fields.js
Outdated
Show resolved
Hide resolved
48415ba
to
4901045
Compare
packages/babel-plugin-proposal-private-property-in-object/src/native-private-fields.js
Outdated
Show resolved
Hide resolved
export default declare((api, options) => { | ||
api.assertVersion(7); | ||
|
||
const { loose, nativePrivateFields } = options; | ||
|
||
if (nativePrivateFields) return pluginPrivateIn(api); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we default nativePrivateFields
to !isRequire("proposal-private-methods", api.targets())
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't necessarily intended to be a public api right since we can just check targets? Or did we want to be able to specify that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally every plugin should automatically compile as few as possible based on the targets, but users should still be able to override it via options.
|
||
function unshadow(name, targetScope, scope) { | ||
while (scope !== targetScope) { | ||
if (scope.hasOwnBinding(name)) scope.rename(name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be easier to rename the class, instead of all conflicting bindings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll leave this as-is: renaming a class is always observable (because it changes the .name
), while renaming the other conflicts is often not observable (if we rename a variable). It can still be observable if the conflict is with a class/function, but at least it's not always.
); | ||
} else { | ||
const id = getWeakSetId( | ||
classWeakSets, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
classWeakSets, | |
staticWeakSets, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is used for instance methods, but it's a single weakset for all the instance methods in the class.
838211b
to
d7d910a
Compare
c5d9896
to
7fcf7c4
Compare
@JLHwung Rather than using |
@nicolo-ribaudo Does the new implementation increase the output size when all class features are compiled? If so I would prefer to use it only when private elements are natively supported. |
When all the class features are compiled we match the old output, because it's handled by the class features helper enabled by one of the other plugins. You can see that bf01056 didn't change any test, except for removing an error. |
Currently we throw an error when compiling private brand checks without compiling private fields. This makes it impossible to add this plugin to
preset-env
, since it will throw an error in browsers with private fields support but without brand checks support.This PR adds a new option to compile private brand checks without relying on the private fields transform:
preset-env
can then enable this option when necessary.