Skip to content

Commit 729adca

Browse files
authored
Merge pull request #14041 from emberjs/optimize-property-reference
[GLIMMER2] Optimize {{property}} PropertyReference
2 parents caf4ab8 + 47511c3 commit 729adca

File tree

10 files changed

+129
-125
lines changed

10 files changed

+129
-125
lines changed

packages/ember-glimmer/lib/component.js

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,10 @@ import ViewMixin from 'ember-views/mixins/view_support';
88
import ActionSupport from 'ember-views/mixins/action_support';
99
import TargetActionSupport from 'ember-runtime/mixins/target_action_support';
1010
import symbol from 'ember-metal/symbol';
11-
import EmptyObject from 'ember-metal/empty_object';
1211
import { get } from 'ember-metal/property_get';
1312
import { PROPERTY_DID_CHANGE } from 'ember-metal/property_events';
1413
import { getAttrFor } from 'ember-views/compat/attrs-proxy';
15-
import {
16-
UPDATE,
17-
TO_ROOT_REFERENCE,
18-
REFERENCE_FOR_KEY,
19-
RootReference,
20-
PropertyReference
21-
} from './utils/references';
14+
import { UPDATE, RootReference } from './utils/references';
2215
import { DirtyableTag } from 'glimmer-reference';
2316
import { readDOMAttr } from 'glimmer-runtime';
2417
import { assert, deprecate } from 'ember-metal/debug';
@@ -28,7 +21,6 @@ import { getOwner } from 'container/owner';
2821
export const DIRTY_TAG = symbol('DIRTY_TAG');
2922
export const ARGS = symbol('ARGS');
3023
export const ROOT_REF = symbol('ROOT_REF');
31-
export const REFS = symbol('REFS');
3224
export const IS_DISPATCHING_ATTRS = symbol('IS_DISPATCHING_ATTRS');
3325
export const HAS_BLOCK = symbol('HAS_BLOCK');
3426

@@ -47,9 +39,9 @@ const Component = CoreView.extend(
4739

4840
init() {
4941
this._super(...arguments);
42+
this[IS_DISPATCHING_ATTRS] = false;
5043
this[DIRTY_TAG] = new DirtyableTag();
51-
this[ROOT_REF] = null;
52-
this[REFS] = new EmptyObject();
44+
this[ROOT_REF] = new RootReference(this);
5345

5446
// If a `defaultLayout` was specified move it to the `layout` prop.
5547
// `layout` is no longer a CP, so this just ensures that the `defaultLayout`
@@ -95,8 +87,6 @@ const Component = CoreView.extend(
9587
this[property.name] = property.descriptor.value;
9688
},
9789

98-
[IS_DISPATCHING_ATTRS]: false,
99-
10090
[PROPERTY_DID_CHANGE](key) {
10191
if (this[IS_DISPATCHING_ATTRS]) { return; }
10292

@@ -118,27 +108,6 @@ const Component = CoreView.extend(
118108

119109
readDOMAttr(name) {
120110
return readDOMAttr(this.element, name);
121-
},
122-
123-
[TO_ROOT_REFERENCE]() {
124-
let ref = this[ROOT_REF];
125-
126-
if (!ref) {
127-
ref = this[ROOT_REF] = new RootReference(this);
128-
}
129-
130-
return ref;
131-
},
132-
133-
[REFERENCE_FOR_KEY](key) {
134-
let refs = this[REFS];
135-
let ref = refs[key];
136-
137-
if (ref) {
138-
return ref;
139-
} else {
140-
return refs[key] = new PropertyReference(this[TO_ROOT_REFERENCE](), key);
141-
}
142111
}
143112
}
144113
);

packages/ember-glimmer/lib/syntax/curly-component.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { StatementSyntax, ValueReference, EvaluatedArgs, EvaluatedNamedArgs, EvaluatedPositionalArgs } from 'glimmer-runtime';
2-
import { TO_ROOT_REFERENCE } from '../utils/references';
32
import { AttributeBinding, ClassNameBinding, IsVisibleBinding } from '../utils/bindings';
4-
import { DIRTY_TAG, IS_DISPATCHING_ATTRS, HAS_BLOCK } from '../component';
3+
import { ROOT_REF, DIRTY_TAG, IS_DISPATCHING_ATTRS, HAS_BLOCK } from '../component';
54
import { assert, runInDebug } from 'ember-metal/debug';
65
import processArgs from '../utils/process-args';
76
import { privatize as P } from 'container/registry';
@@ -224,7 +223,7 @@ class CurlyComponentManager {
224223
}
225224

226225
getSelf({ component }) {
227-
return component[TO_ROOT_REFERENCE]();
226+
return component[ROOT_REF];
228227
}
229228

230229
didCreateElement({ component, classRef }, element, operations) {

packages/ember-glimmer/lib/utils/bindings.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { assert } from 'ember-metal/debug';
22
import { dasherize } from 'ember-runtime/system/string';
33
import { CachedReference, map, referenceFromParts } from 'glimmer-reference';
4-
import { REFERENCE_FOR_KEY, TO_ROOT_REFERENCE } from './references';
4+
import { ROOT_REF } from '../component';
55
import { htmlSafe, isHTMLSafe } from './string';
66

77
function referenceForKey(component, key) {
8-
return component[REFERENCE_FOR_KEY](key);
8+
return component[ROOT_REF].get(key);
99
}
1010

1111
function referenceForParts(component, parts) {
12-
return referenceFromParts(component[TO_ROOT_REFERENCE](), parts);
12+
return referenceFromParts(component[ROOT_REF], parts);
1313
}
1414

1515
export const AttributeBinding = {

packages/ember-glimmer/lib/utils/iterable.js

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -200,42 +200,18 @@ class EachInIterable extends AbstractIterable {
200200

201201
let valueTag = this.valueTag = new UpdatableTag(CONSTANT_TAG);
202202

203-
this.wasProxy = false;
204-
this.proxyWrapperTag = null;
205-
this.proxyContentTag = null;
206-
207203
this.tag = combine([ref.tag, valueTag]);
208204
}
209205

210206
iterate() {
211-
let { ref, keyFor, valueTag, wasProxy } = this;
207+
let { ref, keyFor, valueTag } = this;
212208

213209
let iterable = ref.value();
214210

215-
if (isProxy(iterable)) {
216-
let proxy = iterable;
217-
let content = get(proxy, 'content');
218-
219-
if (wasProxy) {
220-
this.proxyWrapperTag.update(tagFor(proxy));
221-
this.proxyContentTag.update(tagFor(content));
222-
} else {
223-
this.wasProxy = true;
224-
let proxyWrapperTag = this.proxyWrapperTag = new UpdatableTag(tagFor(proxy));
225-
let proxyContentTag = this.proxyContentTag = new UpdatableTag(tagFor(content));
226-
227-
valueTag.update(combine([proxyWrapperTag, proxyContentTag]));
228-
}
229-
230-
iterable = content;
231-
} else {
232-
valueTag.update(tagFor(iterable));
211+
valueTag.update(tagFor(iterable));
233212

234-
if (wasProxy) {
235-
this.wasProxy = true;
236-
this.proxyWrapperTag = null;
237-
this.proxyContentTag = null;
238-
}
213+
if (isProxy(iterable)) {
214+
iterable = get(iterable, 'content');
239215
}
240216

241217
let typeofIterable = typeof iterable;

packages/ember-glimmer/lib/utils/references.js

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { set } from 'ember-metal/property_set';
33
import { tagFor } from 'ember-metal/tags';
44
import { didRender } from 'ember-metal/transaction';
55
import symbol from 'ember-metal/symbol';
6+
import EmptyObject from 'ember-metal/empty_object';
67
import { CONSTANT_TAG, ConstReference, DirtyableTag, UpdatableTag, combine, isConst } from 'glimmer-reference';
78
import { ConditionalReference as GlimmerConditionalReference, NULL_REFERENCE, UNDEFINED_REFERENCE } from 'glimmer-runtime';
89
import emberToBool from './to-bool';
@@ -13,8 +14,6 @@ import isEnabled from 'ember-metal/features';
1314
import { isProxy } from 'ember-runtime/mixins/-proxy';
1415

1516
export const UPDATE = symbol('UPDATE');
16-
export const TO_ROOT_REFERENCE = symbol('TO_ROOT_REFERENCE');
17-
export const REFERENCE_FOR_KEY = symbol('REFERENCE_FOR_KEY');
1817

1918
// @implements PathReference
2019
export class PrimitiveReference extends ConstReference {
@@ -32,7 +31,7 @@ class EmberPathReference {
3231
// @abstract value()
3332

3433
get(key) {
35-
return new PropertyReference(this, key);
34+
return PropertyReference.create(this, key);
3635
}
3736
}
3837

@@ -64,10 +63,19 @@ export class CachedReference extends EmberPathReference {
6463

6564
// @implements PathReference
6665
export class RootReference extends ConstReference {
66+
constructor(value) {
67+
super(value);
68+
this.children = new EmptyObject();
69+
}
70+
6771
get(propertyKey) {
68-
let self = this.value();
69-
let ref = self[REFERENCE_FOR_KEY] && self[REFERENCE_FOR_KEY](propertyKey);
70-
return ref || new PropertyReference(this, propertyKey);
72+
let ref = this.children[propertyKey];
73+
74+
if (!ref) {
75+
ref = this.children[propertyKey] = new RootPropertyReference(this.inner, propertyKey);
76+
}
77+
78+
return ref;
7179
}
7280
}
7381

@@ -106,7 +114,56 @@ if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
106114
};
107115
}
108116

109-
export class PropertyReference extends CachedReference { // jshint ignore:line
117+
export class PropertyReference extends CachedReference {
118+
static create(parentReference, propertyKey) {
119+
if (isConst(parentReference)) {
120+
return new RootPropertyReference(parentReference.value(), propertyKey);
121+
} else {
122+
return new NestedPropertyReference(parentReference, propertyKey);
123+
}
124+
}
125+
126+
get(key) {
127+
return new NestedPropertyReference(this, key);
128+
}
129+
}
130+
131+
export class RootPropertyReference extends PropertyReference {
132+
constructor(parentValue, propertyKey) {
133+
super();
134+
135+
this._parentValue = parentValue;
136+
this._propertyKey = propertyKey;
137+
138+
if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
139+
isEnabled('ember-glimmer-allow-backtracking-rerender')) {
140+
this.tag = new TwoWayFlushDetectionTag(tagFor(parentValue), propertyKey, this);
141+
} else {
142+
this.tag = tagFor(parentValue);
143+
}
144+
145+
if (isEnabled('mandatory-setter')) {
146+
watchKey(parentValue, propertyKey, metaFor(parentValue));
147+
}
148+
}
149+
150+
compute() {
151+
let { _parentValue, _propertyKey } = this;
152+
153+
if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
154+
isEnabled('ember-glimmer-allow-backtracking-rerender')) {
155+
this.tag.didCompute(_parentValue);
156+
}
157+
158+
return get(_parentValue, _propertyKey);
159+
}
160+
161+
[UPDATE](value) {
162+
set(this._parentValue, this._propertyKey, value);
163+
}
164+
}
165+
166+
export class NestedPropertyReference extends PropertyReference {
110167
constructor(parentReference, propertyKey) {
111168
super();
112169

@@ -117,10 +174,6 @@ export class PropertyReference extends CachedReference { // jshint ignore:line
117174
this._parentObjectTag = parentObjectTag;
118175
this._propertyKey = propertyKey;
119176

120-
this._wasProxy = false;
121-
this._proxyWrapperTag = null;
122-
this._proxyContentTag = null;
123-
124177
if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
125178
isEnabled('ember-glimmer-allow-backtracking-rerender')) {
126179
let tag = combine([parentReferenceTag, parentObjectTag]);
@@ -131,32 +184,11 @@ export class PropertyReference extends CachedReference { // jshint ignore:line
131184
}
132185

133186
compute() {
134-
let { _parentReference, _parentObjectTag, _wasProxy, _propertyKey } = this;
187+
let { _parentReference, _parentObjectTag, _propertyKey } = this;
135188

136189
let parentValue = _parentReference.value();
137190

138-
if (isProxy(parentValue)) {
139-
let proxyContent = get(parentValue, 'content');
140-
141-
if (_wasProxy) {
142-
this._proxyWrapperTag.update(tagFor(parentValue));
143-
this._proxyContentTag.update(tagFor(proxyContent));
144-
} else {
145-
this._wasProxy = true;
146-
let _proxyWrapperTag = this._proxyWrapperTag = new UpdatableTag(tagFor(parentValue));
147-
let _proxyContentTag = this._proxyContentTag = new UpdatableTag(tagFor(proxyContent));
148-
149-
_parentObjectTag.update(combine([_proxyWrapperTag, _proxyContentTag]));
150-
}
151-
} else {
152-
_parentObjectTag.update(tagFor(parentValue));
153-
154-
if (_wasProxy) {
155-
this._wasProxy = false;
156-
this._proxyWrapperTag = null;
157-
this._proxyContentTag = null;
158-
}
159-
}
191+
_parentObjectTag.update(tagFor(parentValue));
160192

161193
if (typeof parentValue === 'object' && parentValue) {
162194
if (isEnabled('mandatory-setter')) {
@@ -179,10 +211,6 @@ export class PropertyReference extends CachedReference { // jshint ignore:line
179211
let parent = this._parentReference.value();
180212
set(parent, this._propertyKey, value);
181213
}
182-
183-
get(propertyKey) {
184-
return new PropertyReference(this, propertyKey);
185-
}
186214
}
187215

188216
export class UpdatableReference extends EmberPathReference {
@@ -218,7 +246,9 @@ export class ConditionalReference extends GlimmerConditionalReference {
218246
if (isConst(reference)) {
219247
let value = reference.value();
220248

221-
if (!isProxy(value)) {
249+
if (isProxy(value)) {
250+
return new RootPropertyReference(value, 'isTruthy');
251+
} else {
222252
return new PrimitiveReference(emberToBool(value));
223253
}
224254
}

packages/ember-metal/lib/tags.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { meta as metaFor } from './meta';
22
import require, { has } from 'require';
33

4-
let hasGlimmer = has('glimmer-reference');
4+
const hasGlimmer = has('glimmer-reference');
5+
56
let CONSTANT_TAG, CURRENT_TAG, DirtyableTag, makeTag, run;
67

78
let hasViews = () => false;

packages/ember-metal/lib/transaction.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ if (isEnabled('ember-glimmer-detect-backtracking-rerender') ||
6565
let label;
6666

6767
if (lastRef) {
68-
while (lastRef && lastRef._propertyKey && lastRef._parentReference) {
68+
while (lastRef && lastRef._propertyKey) {
6969
parts.unshift(lastRef._propertyKey);
7070
lastRef = lastRef._parentReference;
7171
}

0 commit comments

Comments
 (0)