Skip to content

Instantly share code, notes, and snippets.

@domenic
Last active December 24, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save domenic/6808845 to your computer and use it in GitHub Desktop.
Save domenic/6808845 to your computer and use it in GitHub Desktop.
DOMPoint desugaring
class Point {
get number x
get number y
get number length
}
class MutablePoint extends Point {
set x(ToNumber)
set y(ToNumber)
}
class Point {
private [[x]], [[y]]
constructor(ToNumber x: [[x]], ToNumber y: [[y]])
get number x: [[x]]
get number y: [[y]]
get number length
}
class MutablePoint extends Point {
set x(ToNumber): [[x]]
set y(ToNumber): [[y]]
}
// Global private variable store.
// Shared among unlimited instances; no real need to segregate by constructor (that I can see).
const privates = new WeakMap();
// Branding helpers
const domPoints = new WeakSet();
function ensureDomPoint(thisP) {
if (!domPoints.has(thisP)) {
throw new TypeError("Called DOMPoint method on incompatible object!");
}
}
class DOMPoint {
[Symbol.create]() {
const newPoint = super();
// Install private state
privates.set(newPoint, { x: undefined, y: undefined });
// Install brand
domPoints.add(newPoint);
return newPoint;
}
constructor(x, y) {
ensureDomPoint(this);
privates.get(this).x = Number(x);
privates.get(this).y = Number(y);
}
// The brand checks are not actually necessary on the getters, but they give better error
// messages than trying to access property `x` of undefined if you just let the next line happen.
get x() {
ensureDomPoint(this);
return privates.get(this).x;
}
get y() {
ensureDomPoint(this);
return privates.get(this).y;
}
get length() {
// This method doesn't use any private state, so it shouldn't check branding.
// It can be applied generically to anything with `x` and `y` properties.
return this.x + this.y;
}
}
// Each class needs its own branding helper
const domPointMutables = new WeakSet();
function ensureDomPointMutable(thisP) {
if (!domPointMutables.has(thisP)) {
throw new TypeError("Called DOMPointMutable method on incompatible object!");
}
}
class DOMPointMutable extends DOMPoint {
[Symbol.create]() {
// By calling super, we ensure that this DOMPointMutable instance is also branded as DOMPoint.
const newPoint = super();
// No new private state
// Install brand
domPointMutables.add(newPoint);
return newPoint;
}
// Brands apply to the setters; they can only be used on DOMPointMutables, not DOMPoints.
set x(value) {
ensureDomPointMutable(this);
privates.get(this).x = Number(value);
}
set y(value) {
ensureDomPointMutable(this);
privates.get(this).y = Number(value);
}
// Getters are inherited, but since they only check the DOMPoint brand, they can work on
// DOMPointMutables too.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment