Skip to content

Commit

Permalink
Merge pull request #16215 from emberjs/move-each-proxy-to-meta
Browse files Browse the repository at this point in the history
Move each proxy into a weak map
  • Loading branch information
rwjblue authored Feb 6, 2018
2 parents fb44213 + 00a7488 commit f243404
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 157 deletions.
5 changes: 2 additions & 3 deletions packages/ember-extension-support/lib/data_adapter.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { getOwner } from 'ember-utils';
import { get, run } from 'ember-metal';
import { get, run, objectAt } from 'ember-metal';
import {
String as StringUtils,
Namespace,
Object as EmberObject,
A as emberA,
addArrayObserver,
removeArrayObserver,
objectAt
removeArrayObserver
} from 'ember-runtime';

/**
Expand Down
11 changes: 8 additions & 3 deletions packages/ember-glimmer/lib/utils/iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import {
UpdatableTag,
} from '@glimmer/reference';
import { Opaque } from '@glimmer/util';
import { get, isProxy, tagFor, tagForProperty } from 'ember-metal';
import {
_contentFor,
isEmberArray,
get,
isProxy,
objectAt,
tagFor,
tagForProperty
} from 'ember-metal';
import {
_contentFor,
isEmberArray
} from 'ember-runtime';
import { guidFor } from 'ember-utils';
import { isEachIn } from '../helpers/each-in';
Expand Down
7 changes: 7 additions & 0 deletions packages/ember-metal/lib/array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function objectAt(content, idx) {
if (typeof content.objectAt === 'function') {
return content.objectAt(idx);
} else {
return content[idx];
}
}
5 changes: 4 additions & 1 deletion packages/ember-metal/lib/chains.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { get } from './property_get';
import { descriptorFor, meta as metaFor, peekMeta } from './meta';
import { watchKey, unwatchKey } from './watch_key';
import { cacheFor } from './computed';
import { eachProxyFor } from './each_proxy';

const FIRST_KEY = /^([^\.]+)/;

Expand Down Expand Up @@ -326,7 +327,9 @@ function lazyGet(obj, key) {
}

// Use `get` if the return value is an EachProxy or an uncacheable value.
if (isVolatile(obj, key, meta)) {
if (key === '@each') {
return eachProxyFor(obj);
} else if (isVolatile(obj, key, meta)) {
return get(obj, key);
// Otherwise attempt to get the cached value of the computed property
} else {
Expand Down
131 changes: 131 additions & 0 deletions packages/ember-metal/lib/each_proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { assert } from 'ember-debug';
import { get } from './property_get';
import { notifyPropertyChange } from './property_events';
import { addObserver, removeObserver } from './observer';
import { meta, peekMeta } from './meta';
import { objectAt } from './array';

const EACH_PROXIES = new WeakMap();

export function eachProxyFor(array) {
let eachProxy = EACH_PROXIES.get(array);
if (eachProxy === undefined) {
eachProxy = new EachProxy(array);
EACH_PROXIES.set(array, eachProxy);
}
return eachProxy;
}

export function eachProxyArrayWillChange(array, idx, removedCnt, addedCnt) {
let eachProxy = EACH_PROXIES.get(array);
if (eachProxy !== undefined) {
eachProxy.arrayWillChange(array, idx, removedCnt, addedCnt);
}
}

export function eachProxyArrayDidChange(array, idx, removedCnt, addedCnt) {
let eachProxy = EACH_PROXIES.get(array);
if (eachProxy !== undefined) {
eachProxy.arrayDidChange(array, idx, removedCnt, addedCnt);
}
}

class EachProxy {
constructor(content) {
this._content = content;
this._keys = undefined;
meta(this);
}

// ..........................................................
// ARRAY CHANGES
// Invokes whenever the content array itself changes.

arrayWillChange(content, idx, removedCnt, addedCnt) { // eslint-disable-line no-unused-vars
let keys = this._keys;
let lim = removedCnt > 0 ? idx + removedCnt : -1;
for (let key in keys) {
if (lim > 0) {
removeObserverForContentKey(content, key, this, idx, lim);
}
}
}

arrayDidChange(content, idx, removedCnt, addedCnt) {
let keys = this._keys;
let lim = addedCnt > 0 ? idx + addedCnt : -1;
let meta = peekMeta(this);
for (let key in keys) {
if (lim > 0) {
addObserverForContentKey(content, key, this, idx, lim);
}
notifyPropertyChange(this, key, meta);
}
}

// ..........................................................
// LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS
// Start monitoring keys based on who is listening...

willWatchProperty(property) {
this.beginObservingContentKey(property);
}

didUnwatchProperty(property) {
this.stopObservingContentKey(property);
}

// ..........................................................
// CONTENT KEY OBSERVING
// Actual watch keys on the source content.

beginObservingContentKey(keyName) {
let keys = this._keys;
if (!keys) {
keys = this._keys = Object.create(null);
}

if (!keys[keyName]) {
keys[keyName] = 1;
let content = this._content;
let len = get(content, 'length');

addObserverForContentKey(content, keyName, this, 0, len);
} else {
keys[keyName]++;
}
}

stopObservingContentKey(keyName) {
let keys = this._keys;
if (keys && (keys[keyName] > 0) && (--keys[keyName] <= 0)) {
let content = this._content;
let len = get(content, 'length');

removeObserverForContentKey(content, keyName, this, 0, len);
}
}

contentKeyDidChange(obj, keyName) {
notifyPropertyChange(this, keyName);
}
}

function addObserverForContentKey(content, keyName, proxy, idx, loc) {
while (--loc >= idx) {
let item = objectAt(content, loc);
if (item) {
assert(`When using @each to observe the array \`${toString(content)}\`, the array must return an object`, typeof item === 'object');
addObserver(item, keyName, proxy, 'contentKeyDidChange');
}
}
}

function removeObserverForContentKey(content, keyName, proxy, idx, loc) {
while (--loc >= idx) {
let item = objectAt(content, loc);
if (item) {
removeObserver(item, keyName, proxy, 'contentKeyDidChange');
}
}
}
2 changes: 2 additions & 0 deletions packages/ember-metal/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export function get(obj: any, keyName: string): any;

export function set(obj: any, keyName: string, value: any, tolerant?: boolean): void;

export function objectAt(arr: any, i: number): any;

export function computed(...args: Array<any>): any;

export function didRender(object: any, key: string, reference: any): boolean;
Expand Down
6 changes: 6 additions & 0 deletions packages/ember-metal/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ export {
set,
trySet
} from './property_set';
export { objectAt } from './array';
export {
eachProxyFor,
eachProxyArrayWillChange,
eachProxyArrayDidChange
} from './each_proxy';
export {
addListener,
hasListeners,
Expand Down
2 changes: 0 additions & 2 deletions packages/ember-runtime/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ export const String: {
loc(s: string, ...args: string[]): string;
};

export function objectAt(arr: any, i: number): any;

export function isEmberArray(arr: any): boolean;

export function _contentFor(proxy: any): any;
1 change: 0 additions & 1 deletion packages/ember-runtime/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export { default as compare } from './compare';
export { default as isEqual } from './is-equal';
export {
default as Array,
objectAt,
isEmberArray,
addArrayObserver,
removeArrayObserver,
Expand Down
Loading

0 comments on commit f243404

Please sign in to comment.