Skip to content
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

Add tracked properties behind a feature flag #16366

Merged
merged 2 commits into from
Mar 13, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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