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.
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.
Ohh I love this idea
There was a problem hiding this comment.
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.
Q: Is _FooBrandCheck.has(other) equivalent to other instanceof Foo?
There was a problem hiding this comment.
No, it's different when other is a subclass of Foo
There was a problem hiding this comment.
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.
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); // trueclass Foo {
#foo;
static check(obj) {
return #foo in obj;
}
}
let foo = new Foo();
foo.__proto__ = {};
foo instanceof Foo; // false
Foo.check(foo); // trueclass Foo {
#foo;
static check(obj) {
return #foo in obj;
}
}
let foo = Object.create(Foo.prototype);
foo instanceof Foo; // true
Foo.check(foo); // falseIf
Bar, a subclass ofFoo, hassuper()in its constructor, does it imply#foo in baristruewherebaris 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.
jridgewell
left a comment
There was a problem hiding this comment.
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! |
48415ba to
4901045
Compare
|
|
||
| const { loose, nativePrivateFields } = options; | ||
|
|
||
| if (nativePrivateFields) return pluginPrivateIn(api); |
There was a problem hiding this comment.
can we default nativePrivateFields to !isRequire("proposal-private-methods", api.targets())?
There was a problem hiding this comment.
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.
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.
Would it be easier to rename the class, instead of all conflicting bindings?
There was a problem hiding this comment.
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.
Nit:
| classWeakSets, | |
| staticWeakSets, |
There was a problem hiding this comment.
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-envcan then enable this option when necessary.