Skip to content
Closed
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
8 changes: 5 additions & 3 deletions packages/ember-metal/lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
@module ember
@submodule ember-metal
*/
import { applyStr } from 'ember-utils';
import { applyStr, functionMetaFor } from 'ember-utils';
import { assert } from './debug';
import { meta as metaFor, peekMeta } from './meta';
import { deprecate } from './debug';

import { ONCE, SUSPENDED } from './meta_listeners';


/*
The event system uses a series of nested hashes to store listeners on an
object. When a listener is registered, or when an event arrives, these
Expand Down Expand Up @@ -304,6 +303,9 @@ export function listenersFor(obj, eventName) {
export function on(...args) {
let func = args.pop();
let events = args;
func.__ember_listens__ = events;

let meta = functionMetaFor(func);
meta.writeListeners(events);

return func;
}
24 changes: 6 additions & 18 deletions packages/ember-metal/lib/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
// Remove "use strict"; from transpiled module until
// https://bugs.webkit.org/show_bug.cgi?id=138038 is fixed

import { EmptyObject, lookupDescriptor, symbol } from 'ember-utils';
import {
EmptyObject,
lookupDescriptor,
symbol,
HAS_NATIVE_WEAKMAP
} from 'ember-utils';
import isEnabled from './features';
import { protoMethods as listenerMethods } from './meta_listeners';
import { runInDebug, assert } from './debug';
Expand Down Expand Up @@ -448,17 +453,6 @@ if (isEnabled('mandatory-setter')) {
};
}

const HAS_NATIVE_WEAKMAP = (function() {
// detect if `WeakMap` is even present
let hasWeakMap = typeof WeakMap === 'function';
if (!hasWeakMap) { return false; }

let instance = new WeakMap();
// use `Object`'s `.toString` directly to prevent us from detecting
// polyfills as native weakmaps
return Object.prototype.toString.call(instance) === '[object WeakMap]';
})();

let setMeta, peekMeta;

// choose the one appropriate for given platform
Expand All @@ -472,12 +466,6 @@ if (HAS_NATIVE_WEAKMAP) {
};

peekMeta = function WeakMap_peekMeta(obj) {
runInDebug(() => counters.peekCalls++);

return metaStore.get(obj);
};

peekMeta = function WeakMap_peekParentMeta(obj) {
let pointer = obj;
let meta;
while (pointer) {
Expand Down
43 changes: 22 additions & 21 deletions packages/ember-metal/lib/mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
guidFor,
GUID_KEY,
wrap,
makeArray
makeArray,
ROOT,
functionMetaFor,
peekFunctionMeta
} from 'ember-utils';
import EmberError from './error';
import {
Expand Down Expand Up @@ -39,9 +42,6 @@ import {
removeListener
} from './events';

function ROOT() {}
ROOT.__hasSuper = false;

const a_slice = [].slice;

function isMethod(obj) {
Expand Down Expand Up @@ -310,29 +310,26 @@ function followAlias(obj, desc, m, descs, values) {
return { desc: desc, value: value };
}

function updateObserversAndListeners(obj, key, observerOrListener, pathsKey, updateMethod) {
let paths = observerOrListener[pathsKey];

if (paths) {
for (let i = 0; i < paths.length; i++) {
updateMethod(obj, paths[i], null, key);
}
function updateObserversAndListeners(obj, key, observerOrListener, paths, updateMethod) {
for (let i = 0; i < paths.length; i++) {
updateMethod(obj, paths[i], null, key);
}
}

function replaceObserversAndListeners(obj, key, observerOrListener) {
let prev = obj[key];
let m;

if ('function' === typeof prev) {
updateObserversAndListeners(obj, key, prev, '__ember_observesBefore__', _removeBeforeObserver);
updateObserversAndListeners(obj, key, prev, '__ember_observes__', removeObserver);
updateObserversAndListeners(obj, key, prev, '__ember_listens__', removeListener);
if ('function' === typeof prev && (m = peekFunctionMeta(prev))) {
updateObserversAndListeners(obj, key, prev, m.peekBeforeObservers(), _removeBeforeObserver);
updateObserversAndListeners(obj, key, prev, m.peekObservers(), removeObserver);
updateObserversAndListeners(obj, key, prev, m.peekListeners(), removeListener);
}

if ('function' === typeof observerOrListener) {
updateObserversAndListeners(obj, key, observerOrListener, '__ember_observesBefore__', _addBeforeObserver);
updateObserversAndListeners(obj, key, observerOrListener, '__ember_observes__', addObserver);
updateObserversAndListeners(obj, key, observerOrListener, '__ember_listens__', addListener);
if ('function' === typeof observerOrListener && (m = peekFunctionMeta(observerOrListener))) {
updateObserversAndListeners(obj, key, observerOrListener, m.peekBeforeObservers(), _addBeforeObserver);
updateObserversAndListeners(obj, key, observerOrListener, m.peekObservers(), addObserver);
updateObserversAndListeners(obj, key, observerOrListener, m.peekListeners(), addListener);
}
}

Expand Down Expand Up @@ -754,7 +751,9 @@ export function observer(...args) {
throw new EmberError('Ember.observer called without a function');
}

func.__ember_observes__ = paths;
let meta = functionMetaFor(func);
meta.writeObservers(paths);

return func;
}

Expand Down Expand Up @@ -838,7 +837,9 @@ export function _beforeObserver(...args) {
throw new EmberError('_beforeObserver called without a function');
}

func.__ember_observesBefore__ = paths;
let meta = functionMetaFor(func);
meta.writeBeforeObservers(paths);

return func;
}

Expand Down
11 changes: 7 additions & 4 deletions packages/ember-runtime/lib/ext/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
observer
} from 'ember-metal';

const a_slice = Array.prototype.slice;
import {
functionMetaFor
} from 'ember-utils';

const FunctionPrototype = Function.prototype;

if (ENV.EXTEND_PROTOTYPES.Function) {
Expand Down Expand Up @@ -184,9 +187,9 @@ if (ENV.EXTEND_PROTOTYPES.Function) {
@for Function
@public
*/
FunctionPrototype.on = function () {
let events = a_slice.call(arguments);
this.__ember_listens__ = events;
FunctionPrototype.on = function (...events) {
let meta = functionMetaFor(this);
meta.writeListeners(events);

return this;
};
Expand Down
133 changes: 133 additions & 0 deletions packages/ember-utils/lib/function-meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { HAS_NATIVE_WEAKMAP } from './weak-map-utils';

function ROOT_WRAPPED_FUNCTION () { }
let ROOT_WRAPPED_FUNCTION_META;

function FunctionMeta(func, wrappedFunctionMeta) {
this._beforeObservers = undefined;
this._observers = undefined;
this._listeners = undefined;
this._hasSuper = undefined;

// The function that we are wrapping
this.wrappedFunctionMeta = wrappedFunctionMeta || ROOT_WRAPPED_FUNCTION_META;
}

FunctionMeta.prototype.hasWrappedFunction = function hasWrappedFunction() {
return this.wrappedFunctionMeta !== ROOT_WRAPPED_FUNCTION_META;
};

FunctionMeta.prototype.peekHasSuper = function hasSuper() {
return this._hasSuper;
};

FunctionMeta.prototype.writeHasSuper = function(value) {
this._hasSuper = value;
};

FunctionMeta.prototype.peekBeforeObservers = function peekBeforeObservers() {
return this._getInherited('_beforeObservers');
};

FunctionMeta.prototype.writeBeforeObservers = function writeBeforeObservers(value) {
this._beforeObservers = value;
};

FunctionMeta.prototype.peekObservers = function peekObservers() {
return this._getInherited('_observers');
};

FunctionMeta.prototype.writeObservers = function writeObservers(value) {
this._observers = value;
};

FunctionMeta.prototype.peekListeners = function peekListeners() {
return this._getInherited('_listeners');
};

FunctionMeta.prototype.writeListeners = function writeListeners(value) {
this._listeners = value;
};

FunctionMeta.prototype._getInherited = function(key) {
let pointer = this;
while (pointer !== undefined) {
if (pointer[key] !== undefined) {
return pointer[key];
}
pointer = pointer.wrappedFunctionMeta;
}
};

function buildRootWrappedFunction() {
let meta = new FunctionMeta(ROOT_WRAPPED_FUNCTION);

meta.writeBeforeObservers([]);
meta.writeObservers([]);
meta.writeListeners([]);

return meta;
}

// setup shared wrapped function
ROOT_WRAPPED_FUNCTION_META = buildRootWrappedFunction();

let setFunctionMeta, peekFunctionMeta, deleteFunctionMeta;

if (HAS_NATIVE_WEAKMAP) {
let metaStore = new WeakMap();

setFunctionMeta = function Fallback_setFunctionMeta(obj, meta) {
metaStore.set(obj, meta);
};

peekFunctionMeta = function Fallback_peekFunctionMeta(obj) {
return metaStore.get(obj);
};

deleteFunctionMeta = function Fallback_deleteFunctionMeta(obj) {
metaStore.set(obj, null);
};
} else {
let FUNCTION_META_FIELD = '__ember_function_meta__';

setFunctionMeta = function Fallback_setFunctionMeta(obj, meta) {
obj[FUNCTION_META_FIELD] = meta;
};

peekFunctionMeta = function Fallback_peekFunctionMeta(obj) {
return obj[FUNCTION_META_FIELD];
};

deleteFunctionMeta = function Fallback_deleteFunctionMeta(obj) {
obj[FUNCTION_META_FIELD] = null;
};
}

/**
Retrieves the meta hash for a function.

A functions meta object contains information about observers, listeners,
if a function has been super wrapped, etc.

@method functionMetaFor
@private

@param {Object} obj The object to retrieve meta for
@return {Object} the meta hash for an object
*/
export function functionMetaFor(func, wrappedFunctionMeta) {
let maybeMeta = peekFunctionMeta(func);
if (maybeMeta) { return maybeMeta; }

let newMeta = new FunctionMeta(func, wrappedFunctionMeta);
setFunctionMeta(func, newMeta);

return newMeta;
}

export {
setFunctionMeta,
peekFunctionMeta,
deleteFunctionMeta
};
2 changes: 2 additions & 0 deletions packages/ember-utils/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ export { canInvoke, tryInvoke } from './invoke';
export { default as makeArray } from './make-array';
export { default as applyStr } from './apply-str';
export { default as toString } from './to-string';
export { functionMetaFor, peekFunctionMeta } from './function-meta';
export { HAS_NATIVE_WEAKMAP } from './weak-map-utils';
Loading