Skip to content

Commit

Permalink
Merge pull request #16366 from emberjs/tracked
Browse files Browse the repository at this point in the history
Add tracked properties behind a feature flag
  • Loading branch information
rwjblue committed Mar 13, 2018
2 parents 66c39c2 + a456200 commit dd93361
Show file tree
Hide file tree
Showing 15 changed files with 1,037 additions and 19 deletions.
3 changes: 2 additions & 1 deletion features.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"ember-engines-mount-params": true,
"ember-module-unification": null,
"glimmer-custom-component-manager": null,
"ember-template-block-let-helper": null
"ember-template-block-let-helper": null,
"ember-metal-tracked-properties": null
},
"deprecations": {
"container-lookupFactory": "2.12.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,8 @@ moduleFor('Application test: engine rendering', class extends ApplicationTest {
let href1337 = this.element.querySelector('.author-1337').href;

// check if link ends with the suffix
assert.ok(this.stringsEndWith(href1, suffix1));
assert.ok(this.stringsEndWith(href1337, suffix1337));
assert.ok(this.stringsEndWith(href1, suffix1), `${href1} ends with ${suffix1}`);
assert.ok(this.stringsEndWith(href1337, suffix1337), `${href1337} ends with ${suffix1337}`);
});
}

Expand Down
12 changes: 12 additions & 0 deletions packages/ember-metal/externs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
declare module 'ember/features' {
export const EMBER_TEMPLATE_BLOCK_LET_HELPER: boolean | null;
export const EMBER_MODULE_UNIFICATION: boolean | null;
export const GLIMMER_CUSTOM_COMPONENT_MANAGER: boolean | null;
export const EMBER_ENGINES_MOUNT_PARAMS: boolean | null;
export const EMBER_GLIMMER_DETECT_BACKTRACKING_RERENDER: boolean | null;
export const MANDATORY_SETTER: boolean | null;
}

declare module 'ember-env-flags' {
export const DEBUG: boolean;
}
84 changes: 82 additions & 2 deletions packages/ember-metal/lib/computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { inspect } from 'ember-utils';
import { assert, warn, Error as EmberError } from 'ember-debug';
import { set } from './property_set';
import { meta as metaFor, peekMeta } from './meta';
import { EMBER_METAL_TRACKED_PROPERTIES } from 'ember/features';
import expandProperties from './expand_properties';
import {
Descriptor,
Expand All @@ -14,6 +15,8 @@ import {
addDependentKeys,
removeDependentKeys
} from './dependent_keys';
import { getCurrentTracker, setCurrentTracker } from './tracked';
import { tagForProperty, update } from './tags';

/**
@module @ember/object
Expand Down Expand Up @@ -150,6 +153,10 @@ class ComputedProperty extends Descriptor {
this._meta = undefined;
this._volatile = false;

if (EMBER_METAL_TRACKED_PROPERTIES) {
this._auto = false;
}

this._dependentKeys = opts && opts.dependentKeys;
this._readOnly = opts && hasGetterOnly && opts.readOnly === true;
}
Expand Down Expand Up @@ -328,13 +335,48 @@ class ComputedProperty extends Descriptor {
}

let cache = getCacheFor(obj);
let propertyTag;

if (EMBER_METAL_TRACKED_PROPERTIES) {
propertyTag = tagForProperty(obj, keyName);

if (cache.has(keyName)) {
// special-case for computed with no dependent keys used to
// trigger cacheable behavior.
if (!this._auto && (!this._dependentKeys || this._dependentKeys.length === 0)) {
return cache.get(keyName);
}

let lastRevision = getLastRevisionFor(obj, keyName);
if (propertyTag.validate(lastRevision)) {
return cache.get(keyName);
}
}
} else {
if (cache.has(keyName)) {
return cache.get(keyName);
}
}

let parent;
let tracker;

if (cache.has(keyName)) {
return cache.get(keyName);
if (EMBER_METAL_TRACKED_PROPERTIES) {
parent = getCurrentTracker();
tracker = setCurrentTracker();
}

let ret = this._getter.call(obj, keyName);

if (EMBER_METAL_TRACKED_PROPERTIES) {
setCurrentTracker(parent);
let tag = tracker.combine();
if (parent) parent.add(tag);

update(propertyTag, tag);
setLastRevisionFor(obj, keyName, propertyTag.value());
}

cache.set(keyName, ret);

let meta = metaFor(obj);
Expand Down Expand Up @@ -409,6 +451,11 @@ class ComputedProperty extends Descriptor {

notifyPropertyChange(obj, keyName, meta);

if (EMBER_METAL_TRACKED_PROPERTIES) {
let propertyTag = tagForProperty(obj, keyName);
setLastRevisionFor(obj, keyName, propertyTag.value());
}

return ret;
}

Expand All @@ -423,6 +470,14 @@ class ComputedProperty extends Descriptor {
}
}
}

if (EMBER_METAL_TRACKED_PROPERTIES) {
ComputedProperty.prototype.auto = function() {
this._auto = true;
return this;
};
}

/**
This helper returns a new property descriptor that wraps the passed
computed property function. You can use this helper to define properties
Expand Down Expand Up @@ -524,6 +579,7 @@ export default function computed(...args) {
}

const COMPUTED_PROPERTY_CACHED_VALUES = new WeakMap();
const COMPUTED_PROPERTY_LAST_REVISION = EMBER_METAL_TRACKED_PROPERTIES ? new WeakMap() : undefined;

/**
Returns the cached value for a property, if one exists.
Expand All @@ -544,6 +600,11 @@ export function getCacheFor(obj) {
let cache = COMPUTED_PROPERTY_CACHED_VALUES.get(obj);
if (cache === undefined) {
cache = new Map();

if (EMBER_METAL_TRACKED_PROPERTIES) {
COMPUTED_PROPERTY_LAST_REVISION.set(obj, new Map());
}

COMPUTED_PROPERTY_CACHED_VALUES.set(obj, cache);
}
return cache;
Expand All @@ -556,6 +617,25 @@ export function getCachedValueFor(obj, key) {
}
}

export let setLastRevisionFor;
export let getLastRevisionFor;

if (EMBER_METAL_TRACKED_PROPERTIES) {
setLastRevisionFor = (obj, key, revision) => {
let lastRevision = COMPUTED_PROPERTY_LAST_REVISION.get(obj);
lastRevision.set(key, revision);
};

getLastRevisionFor = (obj, key) => {
let cache = COMPUTED_PROPERTY_LAST_REVISION.get(obj);
if (cache == undefined) {
return 0;
} else {
return cache.get(key);
}
};
}

export function peekCacheFor(obj) {
return COMPUTED_PROPERTY_CACHED_VALUES.get(obj);
}
Expand Down
1 change: 1 addition & 0 deletions packages/ember-metal/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,4 @@ export {
setProxy
} from './is_proxy';
export { default as descriptor } from './descriptor';
export { tracked } from './tracked';
9 changes: 8 additions & 1 deletion packages/ember-metal/lib/property_get.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

import { assert, deprecate } from 'ember-debug';
import { HAS_NATIVE_PROXY, symbol } from 'ember-utils';
import { DESCRIPTOR_TRAP, EMBER_METAL_ES5_GETTERS, MANDATORY_GETTER } from 'ember/features';
import { DESCRIPTOR_TRAP, EMBER_METAL_ES5_GETTERS, EMBER_METAL_TRACKED_PROPERTIES, MANDATORY_GETTER } from 'ember/features';
import { isPath } from './path_cache';
import { isDescriptor, isDescriptorTrap, DESCRIPTOR, descriptorFor } from './meta';
import { getCurrentTracker } from './tracked';
import { tagForProperty } from './tags';

const ALLOWABLE_TYPES = {
object: true,
Expand Down Expand Up @@ -86,6 +88,11 @@ export function get(obj, keyName) {
let value;

if (isObjectLike) {
if (EMBER_METAL_TRACKED_PROPERTIES) {
let tracker = getCurrentTracker();
if (tracker) tracker.add(tagForProperty(obj, keyName));
}

if (EMBER_METAL_ES5_GETTERS) {
descriptor = descriptorFor(obj, keyName);
}
Expand Down
31 changes: 28 additions & 3 deletions packages/ember-metal/lib/tags.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CONSTANT_TAG, DirtyableTag } from '@glimmer/reference';
import { CONSTANT_TAG, UpdatableTag, DirtyableTag, combine } from '@glimmer/reference';
import { EMBER_METAL_TRACKED_PROPERTIES } from 'ember/features';
import { meta as metaFor } from './meta';
import { isProxy } from './is_proxy';
import run from './run_loop';
Expand All @@ -13,6 +14,8 @@ function makeTag() {
return DirtyableTag.create();
}

export const TRACKED_GETTERS = EMBER_METAL_TRACKED_PROPERTIES ? new WeakMap() : undefined;

export function tagForProperty(object, propertyKey, _meta) {
if (typeof object !== 'object' || object === null) { return CONSTANT_TAG; }

Expand All @@ -25,7 +28,12 @@ export function tagForProperty(object, propertyKey, _meta) {
let tag = tags[propertyKey];
if (tag) { return tag; }

return tags[propertyKey] = makeTag();
if (EMBER_METAL_TRACKED_PROPERTIES) {
let pair = combine([makeTag(), UpdatableTag.create(CONSTANT_TAG)]);
return tags[propertyKey] = pair;
} else {
return tags[propertyKey] = makeTag();
}
}

export function tagFor(object, _meta) {
Expand All @@ -37,6 +45,23 @@ export function tagFor(object, _meta) {
}
}

export let dirty;
export let update;

if (EMBER_METAL_TRACKED_PROPERTIES) {
dirty = (tag) => {
tag.inner.first.inner.dirty();
};

update = (outer, inner) => {
outer.inner.second.inner.update(inner);
};
} else {
dirty = (tag) => {
tag.inner.dirty();
};
}

export function markObjectAsDirty(obj, propertyKey, meta) {
let objectTag = meta.readableTag();

Expand All @@ -52,7 +77,7 @@ export function markObjectAsDirty(obj, propertyKey, meta) {
let propertyTag = tags !== undefined ? tags[propertyKey] : undefined;

if (propertyTag !== undefined) {
propertyTag.inner.dirty();
dirty(propertyTag);
}

if (objectTag !== undefined || propertyTag !== undefined) {
Expand Down
Loading

0 comments on commit dd93361

Please sign in to comment.