Skip to content

Commit

Permalink
Refactor to use built-in on Ember 3.20
Browse files Browse the repository at this point in the history
  • Loading branch information
rwjblue committed Jul 26, 2020
1 parent b27d7c7 commit 8436ae5
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 16 deletions.
79 changes: 78 additions & 1 deletion addon/-internal/destructors.ts
@@ -1,6 +1,9 @@
import { assert } from '@ember/debug';
import { schedule } from '@ember/runloop';
import { DEBUG } from '@glimmer/env';
import Ember from 'ember';

import { gte } from 'ember-compatibility-helpers';

import { meta, deleteMeta } from './meta';

Expand All @@ -15,6 +18,29 @@ let DESTROYABLE_PARENTS:
| WeakMap<object, object> = new WeakMap<object, object>();
const DESTROYABLE_CHILDREN = new WeakMap<object, Set<object>>();

let _internalRegisterDestructor: Function;
let _internalAssociateDestroyableChild: Function;
let _internalIsDestroying: Function;
let _internalIsDestroyed: Function;
let _internalUnregisterDestructor: Function;
let _internalDestroy: Function;
let _internalAssertDestroyablesDestroyed: Function;
let _internalEnableDestroyableTracking: Function;

if (gte('3.20.0-beta.4')) {
const glimmerRuntime = (Ember as any).__loader.require('@glimmer/runtime');

_internalRegisterDestructor = glimmerRuntime.registerDestructor;
_internalAssociateDestroyableChild = glimmerRuntime.associateDestroyableChild;
_internalIsDestroying = glimmerRuntime.isDestroying;
_internalIsDestroyed = glimmerRuntime.isDestroyed;
_internalUnregisterDestructor = glimmerRuntime.unregisterDestructor;
_internalDestroy = glimmerRuntime.destroy;
_internalAssertDestroyablesDestroyed =
glimmerRuntime.assertDestroyablesDestroyed;
_internalEnableDestroyableTracking = glimmerRuntime.enableDestroyableTracking;
}

function getDestructors<T extends object>(destroyable: T): Set<Destructor<T>> {
if (!DESTRUCTORS.has(destroyable)) DESTRUCTORS.set(destroyable, new Set());
return DESTRUCTORS.get(destroyable)!;
Expand All @@ -39,6 +65,10 @@ function getDestroyableChildren(destroyable: object): Set<object> {
* ```
*/
export function isDestroying(destroyable: object): boolean {
if (gte('3.20.0-beta.4')) {
return _internalIsDestroying(destroyable);
}

return meta(destroyable).isSourceDestroying();
}

Expand All @@ -56,6 +86,10 @@ export function isDestroying(destroyable: object): boolean {
* ```
*/
export function isDestroyed(destroyable: object): boolean {
if (gte('3.20.0-beta.4')) {
return _internalIsDestroyed(destroyable);
}

return meta(destroyable).isSourceDestroyed();
}

Expand Down Expand Up @@ -97,6 +131,10 @@ export function associateDestroyableChild<T extends object>(
parent: object,
child: T
): T {
if (gte('3.20.0-beta.4')) {
return _internalAssociateDestroyableChild(parent, child);
}

if (DEBUG) assertNotDestroyed(parent);
if (DEBUG) assertNotDestroyed(child);

Expand Down Expand Up @@ -152,6 +190,10 @@ export function registerDestructor<T extends object>(
destroyable: T,
destructor: Destructor<T>
): Destructor<T> {
if (gte('3.20.0-beta.4')) {
return _internalRegisterDestructor(destroyable, destructor);
}

if (DEBUG) assertNotDestroyed(destroyable);
const destructors = getDestructors(destroyable);
assert(
Expand Down Expand Up @@ -193,6 +235,10 @@ export function unregisterDestructor<T extends object>(
destroyable: T,
destructor: Destructor<T>
): void {
if (gte('3.20.0-beta.4')) {
return _internalUnregisterDestructor(destroyable, destructor);
}

if (DEBUG) assertNotDestroyed(destroyable);
const destructors = getDestructors(destroyable);
assert(
Expand Down Expand Up @@ -237,6 +283,11 @@ export function unregisterDestructor<T extends object>(
*
*/
export function destroy(destroyable: object): void {
if (gte('3.20.0-beta.4')) {
_internalDestroy(destroyable);
return;
}

if (isDestroying(destroyable) || isDestroyed(destroyable)) return;

const m = meta(destroyable);
Expand Down Expand Up @@ -275,6 +326,19 @@ interface UndestroyedDestroyablesAssertionError extends Error {
* assertDestroyablesDestroyed later.
*/
export function enableDestroyableTracking() {
if (gte('3.20.2')) {
return _internalEnableDestroyableTracking();
}
if (gte('3.20.0-beta.4')) {
// on 3.20.0-beta.4 through 3.20.2 (estimated) there is an issue with the upstream
// `assertDestroyablesDestroyed` method that triggers the assertion in cases that it
// should not; in order to allow code bases to function on those specific Ember versions
// (including our own test suite) we detect and do nothing
//
// See https://github.com/glimmerjs/glimmer-vm/pull/1119
return;
}

DESTRUCTORS = new Map<object, Set<Destructor>>();
DESTROYABLE_PARENTS = new Map<object, object>();
isTesting = true;
Expand All @@ -288,6 +352,19 @@ export function enableDestroyableTracking() {
* fact been destroyed.
*/
export function assertDestroyablesDestroyed(): void | never {
if (gte('3.20.2')) {
return _internalAssertDestroyablesDestroyed();
}
if (gte('3.20.0-beta.4')) {
// on 3.20.0-beta.4 through 3.20.2 (estimated) there is an issue with the upstream
// `assertDestroyablesDestroyed` method that triggers the assertion in cases that it
// should not; in order to allow code bases to function on those specific Ember versions
// (including our own test suite) we detect and do nothing
//
// See https://github.com/glimmerjs/glimmer-vm/pull/1119
return;
}

if (!isTesting) {
throw new Error(
'Attempted to assert destroyables destroyed, but you did not start a destroyable test. Did you forget to call `enableDestroyableTracking()`'
Expand All @@ -303,7 +380,7 @@ export function assertDestroyablesDestroyed(): void | never {

if (destructors.size > 0 || children.size > 0) {
const error = new Error(
`Not all destroyable objects were destroyed`
`Some destroyables were not destroyed during this test`
) as UndestroyedDestroyablesAssertionError;

Object.defineProperty(error, 'destroyables', {
Expand Down
1 change: 0 additions & 1 deletion addon/-internal/meta.ts
@@ -1,5 +1,4 @@
/* eslint-disable no-bitwise */
// import { gte } from 'ember-compatibility-helpers';
import Ember from 'ember';

import { gte } from 'ember-compatibility-helpers';
Expand Down
20 changes: 12 additions & 8 deletions addon/-internal/patch-core-object.ts
@@ -1,14 +1,18 @@
import { gte } from 'ember-compatibility-helpers';

import CoreObject from '@ember/object/core';

import { destroy as _destroy, registerDestructor } from '..';

const callWillDestroy = (instance: CoreObject) => instance.willDestroy();
if (!gte('3.20.0-beta.4')) {
const callWillDestroy = (instance: CoreObject) => instance.willDestroy();

CoreObject.prototype.init = function init() {
registerDestructor(this, callWillDestroy);
};
CoreObject.prototype.init = function init() {
registerDestructor(this, callWillDestroy);
};

CoreObject.prototype.destroy = function destroy() {
_destroy(this);
return this;
};
CoreObject.prototype.destroy = function destroy() {
_destroy(this);
return this;
};
}
14 changes: 9 additions & 5 deletions addon/-internal/patch-meta.ts
@@ -1,9 +1,13 @@
import { gte } from 'ember-compatibility-helpers';

import { runDestructors } from './destructors';
import { Meta } from './meta';

const { setSourceDestroying } = Meta.prototype;
if (!gte('3.20.0-beta.1')) {
const { setSourceDestroying } = Meta.prototype;

Meta.prototype.setSourceDestroying = function () {
setSourceDestroying.call(this);
runDestructors(this.source);
};
Meta.prototype.setSourceDestroying = function () {
setSourceDestroying.call(this);
runDestructors(this.source);
};
}
14 changes: 13 additions & 1 deletion tests/unit/destroyable-test.ts
Expand Up @@ -14,6 +14,8 @@ import {
import CoreObject from '@ember/object/core';
import { run } from '@ember/runloop';

import { gte } from 'ember-compatibility-helpers';

function makeDestructor(
assert: Assert,
step: string,
Expand Down Expand Up @@ -247,6 +249,16 @@ module('destroyable', function (_hooks) {
});

module('assertDestroyablesDestroyed', function () {
if (gte('3.20.0-beta.4') && !gte('3.20.2')) {
// on 3.20.0-beta.4 through 3.20.2 (estimated) there is an issue with the upstream
// `assertDestroyablesDestroyed` method that triggers the assertion in cases that it
// should not; in order to allow code bases to function on those specific Ember versions
// (including our own test suite) we detect and do nothing
//
// See https://github.com/glimmerjs/glimmer-vm/pull/1119
return;
}

test('it does not throw an error when destroyables have been destroyed', async function (assert) {
assert.expect(1);

Expand Down Expand Up @@ -286,7 +298,7 @@ module('destroyable', function (_hooks) {

assert.throws(() => {
assertDestroyablesDestroyed();
}, /Not all destroyable objects were destroyed/);
}, /Some destroyables were not destroyed during this test/);
});

test('errors if `enableDestroyableTracking` was not called previously', async function (assert) {
Expand Down
9 changes: 9 additions & 0 deletions yarn.lock
Expand Up @@ -5431,6 +5431,15 @@ ember-compatibility-helpers@^1.1.2, ember-compatibility-helpers@^1.2.0, ember-co
ember-cli-version-checker "^2.1.1"
semver "^5.4.1"

ember-compatibility-helpers@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.1.tgz#87c92c4303f990ff455c28ca39fb3ee11441aa16"
integrity sha512-6wzYvnhg1ihQUT5yGqnLtleq3Nv5KNv79WhrEuNU9SwR4uIxCO+KpyC7r3d5VI0EM7/Nmv9Nd0yTkzmTMdVG1A==
dependencies:
babel-plugin-debug-macros "^0.2.0"
ember-cli-version-checker "^2.1.1"
semver "^5.4.1"

ember-disable-prototype-extensions@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/ember-disable-prototype-extensions/-/ember-disable-prototype-extensions-1.1.3.tgz#1969135217654b5e278f9fe2d9d4e49b5720329e"
Expand Down

0 comments on commit 8436ae5

Please sign in to comment.