Skip to content
This repository has been archived by the owner on May 25, 2021. It is now read-only.

Commit

Permalink
Remove MD5 reference (was used for path hashing)
Browse files Browse the repository at this point in the history
Object metadata cannot be based on paths, has to be based on reference, because the same object can appear at multiple paths
  - Luckily our serializer retains object references across serializations, so this can work for us
  • Loading branch information
clbond committed Nov 8, 2016
1 parent 754a34c commit 58743ed
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 45 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
"es6-promise": "^3.1.2",
"es6-shim": "^0.35.0",
"file-loader": "^0.8.5",
"md5": "^2.2.1",
"msgpack-lite": "^0.1.20",
"object-assign": "4.0.1",
"postcss-cssnext": "^2.5.2",
Expand Down
4 changes: 1 addition & 3 deletions src/frontend/components/render-state/render-state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import md5 = require('md5');

import {
ChangeDetectionStrategy,
Component,
Expand Down Expand Up @@ -125,7 +123,7 @@ export class RenderState {
}

private getMetadata(key: string): [ObjectType, any] {
return this.metadata.get(md5(serializePath(this.path.concat([key]))));
return this.metadata.get(this.state[key]);
}

private isEmittable(key: string): boolean {
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ class App {
if (typeof beforeLoad === 'function') {
beforeLoad();
}
return response;

const {instance, metadata} = response;

return {instance, metadata: new Map(metadata)};
});

this.componentState.wait(node, promise);
Expand Down
51 changes: 21 additions & 30 deletions src/tree/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import md5 = require('md5');

import {
AsyncSubject,
BehaviorSubject,
Expand All @@ -9,12 +7,6 @@ import {
Subject,
} from 'rxjs';

import {
Path,
serializePath,
deserializePath,
} from './path';

import {Node} from './node';

import {
Expand Down Expand Up @@ -42,53 +34,54 @@ export enum ObjectType {
ContentChildren = 0x100,
}

export type Metadata = Map<string, [ObjectType, any]>;
export type Metadata = Map<any, [ObjectType, any]>;

export interface InstanceWithMetadata {
instance: any;
metadata: Metadata;
}

export const instanceWithMetadata = (node: Node, instance): InstanceWithMetadata => {
// It is imperative that the metadata and the instance value itself travel together
// through the serializer, otherwise we are going to have to serialize the entire
// object structure twice, once for the instance and once for the metadata. But if
// we put them together as part of the same object, the serializer will be smart
// enough not to duplicate objects. If someone breaks apart the instance and the
// metadata into two objects, a lot of code that depends on reference equality is
// going to get broken! So do not change this!
export const instanceWithMetadata = (node: Node, instance) => {
if (node == null || instance == null) {
return null;
}

const metadata = new Map<string, [ObjectType, any]>();

const nodePath = deserializePath(node.id);

const serialize = (path: Path): string => md5(serializePath(path));

recurse(nodePath, instance,
(path: Path, obj) => {
recurse(instance,
obj => {
let type = objectType(obj);

const update = (p: Path, flag: ObjectType, additionalProps) => {
const serializedPath = serialize(p);

const existing = metadata.get(serializedPath);
const update = (flag: ObjectType, additionalProps) => {
const existing = metadata.get(obj);
if (existing) {
existing[0] |= flag;
Object.assign(existing, additionalProps);
}
else {
metadata.set(serializedPath, [flag, additionalProps]);
metadata.set(obj, [flag, additionalProps]);
}
};

const component = componentMetadata(obj);
if (component) {
for (const input of componentInputs(component, obj)) {
update(path.concat([input.propertyKey]), ObjectType.Input, {alias: input.bindingPropertyName});
update(ObjectType.Input, {alias: input.bindingPropertyName});
}
for (const output of componentOutputs(component, obj)) {
update(path.concat([output.propertyKey]), ObjectType.Output, {alias: output.bindingPropertyName});
update(ObjectType.Output, {alias: output.bindingPropertyName});
}

const addQuery = (decoratorType: string, objectType: ObjectType) => {
for (const vc of componentQueryChildren(decoratorType, component, obj)) {
update(path.concat([vc.propertyKey]), objectType, {selector: vc.selector});
update(objectType, {selector: vc.selector});
}
};

Expand All @@ -99,19 +92,17 @@ export const instanceWithMetadata = (node: Node, instance): InstanceWithMetadata
}

if (type !== 0) {
const serializedPath = serialize(path);

const existing = metadata.get(serializedPath);
const existing = metadata.get(obj);
if (existing) {
metadata.set(serializedPath, [existing[0] | type, existing[1]]);
metadata.set(obj, [existing[0] | type, existing[1]]);
}
else {
metadata.set(serializedPath, [type, null]);
metadata.set(obj, [type, null]);
}
}
});

return {instance, metadata};
return {instance, metadata: Array.from(<any> metadata)};
};

const objectType = (object): ObjectType => {
Expand Down
25 changes: 16 additions & 9 deletions src/utils/circular-recurse.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import {Path} from '../tree';

export type Apply = (path: Path, value) => void;
import {isScalar} from './scalar';

export const recurse = (initialPath: Path, object, exec: Apply) => {
export type Apply = (value) => void;

// Recursive traversal of an object tree, but will not traverse circular references or DOM elements
export const recurse = (object, apply: Apply) => {
const visited = new Set();

const apply = (path: Path, value, fn: Apply) => {
if (value == null || visited.has(value)) {
const visit = value => {
if (value == null || isScalar(value) || /Element/.test(Object.prototype.toString.call(value))) {
return;
}

if (visited.has(value)) { // circular loop
return;
}

visited.add(value);

fn(path, value);
apply(value);

if (Array.isArray(value) || value instanceof Set) {
(<any>value).forEach((v, k) => apply(path.concat([k]), v, fn));
(<any>value).forEach((v, k) => visit(v));
}
else if (value instanceof Map) {
value.forEach((v, k) => apply(path.concat([k]), v, fn));
value.forEach((v, k) => visit(v));
}
else {
Object.keys(value).forEach(k => apply(path.concat([k]), value[k], fn));
Object.keys(value).forEach(k => visit(value[k]));
}
};

apply(initialPath, object, exec);
visit(object);
};
1 change: 1 addition & 0 deletions src/utils/scalar.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const isScalar = value => {
switch (typeof value) {
case 'string':
case 'number':
case 'boolean':
case 'function':
case 'undefined':
Expand Down
1 change: 0 additions & 1 deletion typings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504",
"filesystem": "registry:dt/filesystem#0.0.0+20160316155526",
"filewriter": "registry:dt/filewriter#0.0.0+20160316155526",
"md5": "registry:dt/md5#2.1.0+20160521153251",
"node": "registry:dt/node#6.0.0+20160823142437",
"tape": "registry:dt/tape#4.2.2+20160317120654",
"webrtc/mediastream": "registry:dt/webrtc/mediastream#0.0.0+20160317120654"
Expand Down

0 comments on commit 58743ed

Please sign in to comment.