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

Support multiple edge types in Graph.hasEdge #8550

Merged
merged 15 commits into from Oct 19, 2022
4 changes: 3 additions & 1 deletion packages/bundlers/experimental/src/ExperimentalBundler.js
Expand Up @@ -329,7 +329,9 @@ function createIdealGraph(
let bundleRootGraph: ContentGraph<
BundleRoot | 'root',
$Values<typeof bundleRootEdgeTypes>,
> = new ContentGraph();
> = new ContentGraph({
edgeTypes: dependencyPriorityEdges,
mattcompiles marked this conversation as resolved.
Show resolved Hide resolved
});

let bundleGroupBundleIds: Set<NodeId> = new Set();

Expand Down
6 changes: 4 additions & 2 deletions packages/core/core/src/AssetGraph.js
Expand Up @@ -136,8 +136,10 @@ export default class AssetGraph extends ContentGraph<AssetGraphNode> {
}

// $FlowFixMe[prop-missing]
static deserialize(opts: AssetGraphOpts): AssetGraph {
return new AssetGraph(opts);
static deserialize(opts: SerializedAssetGraph): AssetGraph {
return new AssetGraph({
...opts,
});
}

// $FlowFixMe[prop-missing]
Expand Down
14 changes: 4 additions & 10 deletions packages/core/core/src/BundleGraph.js
Expand Up @@ -78,14 +78,6 @@ type InternalExportSymbolResolution = {|
+exportAs: Symbol | string,
|};

type BundleGraphOpts = {|
graph: ContentGraphOpts<BundleGraphNode, BundleGraphEdgeType>,
bundleContentHashes: Map<string, string>,
assetPublicIds: Set<string>,
publicIdByAssetId: Map<string, string>,
symbolPropagationRan: boolean,
|};

type SerializedBundleGraph = {|
$$raw: true,
graph: SerializedContentGraph<BundleGraphNode, BundleGraphEdgeType>,
Expand Down Expand Up @@ -146,7 +138,9 @@ export default class BundleGraph {
publicIdByAssetId: Map<string, string> = new Map(),
assetPublicIds: Set<string> = new Set(),
): BundleGraph {
let graph = new ContentGraph<BundleGraphNode, BundleGraphEdgeType>();
let graph = new ContentGraph<BundleGraphNode, BundleGraphEdgeType>({
edgeTypes: bundleGraphEdgeTypes,
});
let assetGroupIds = new Map();
let dependencies = new Map();
let assetGraphNodeIdToBundleGraphNodeId = new Map<NodeId, NodeId>();
Expand Down Expand Up @@ -362,7 +356,7 @@ export default class BundleGraph {
};
}

static deserialize(serialized: BundleGraphOpts): BundleGraph {
static deserialize(serialized: SerializedBundleGraph): BundleGraph {
return new BundleGraph({
graph: ContentGraph.deserialize(serialized.graph),
assetPublicIds: serialized.assetPublicIds,
Expand Down
50 changes: 28 additions & 22 deletions packages/core/core/src/RequestTracker.js
Expand Up @@ -62,17 +62,6 @@ export const requestGraphEdgeTypes = {

export type RequestGraphEdgeType = $Values<typeof requestGraphEdgeTypes>;

type RequestGraphOpts = {|
...ContentGraphOpts<RequestGraphNode, RequestGraphEdgeType>,
invalidNodeIds: Set<NodeId>,
incompleteNodeIds: Set<NodeId>,
globNodeIds: Set<NodeId>,
envNodeIds: Set<NodeId>,
optionNodeIds: Set<NodeId>,
unpredicatableNodeIds: Set<NodeId>,
invalidateOnBuildNodeIds: Set<NodeId>,
|};

type SerializedRequestGraph = {|
...SerializedContentGraph<RequestGraphNode, RequestGraphEdgeType>,
invalidNodeIds: Set<NodeId>,
Expand Down Expand Up @@ -221,18 +210,35 @@ export class RequestGraph extends ContentGraph<
unpredicatableNodeIds: Set<NodeId> = new Set();
invalidateOnBuildNodeIds: Set<NodeId> = new Set();

constructor(opts?: SerializedRequestGraph) {
if (opts) {
let {
invalidNodeIds,
incompleteNodeIds,
globNodeIds,
envNodeIds,
optionNodeIds,
unpredicatableNodeIds,
invalidateOnBuildNodeIds,
...rest
} = opts;
super({edgeTypes: requestGraphEdgeTypes, ...rest});

this.invalidNodeIds = invalidNodeIds;
this.incompleteNodeIds = incompleteNodeIds;
this.globNodeIds = globNodeIds;
this.envNodeIds = envNodeIds;
this.optionNodeIds = optionNodeIds;
this.unpredicatableNodeIds = unpredicatableNodeIds;
this.invalidateOnBuildNodeIds = invalidateOnBuildNodeIds;
} else {
super({edgeTypes: requestGraphEdgeTypes});
}
}

// $FlowFixMe[prop-missing]
static deserialize(opts: RequestGraphOpts): RequestGraph {
// $FlowFixMe[prop-missing]
let deserialized = new RequestGraph(opts);
deserialized.invalidNodeIds = opts.invalidNodeIds;
deserialized.incompleteNodeIds = opts.incompleteNodeIds;
deserialized.globNodeIds = opts.globNodeIds;
deserialized.envNodeIds = opts.envNodeIds;
deserialized.optionNodeIds = opts.optionNodeIds;
deserialized.unpredicatableNodeIds = opts.unpredicatableNodeIds;
deserialized.invalidateOnBuildNodeIds = opts.invalidateOnBuildNodeIds;
return deserialized;
static deserialize(opts: SerializedRequestGraph): RequestGraph {
return new RequestGraph(opts);
}

// $FlowFixMe[prop-missing]
Expand Down
42 changes: 37 additions & 5 deletions packages/core/graph/src/AdjacencyList.js
Expand Up @@ -18,12 +18,14 @@ opaque type EdgeAddress = number;
export type SerializedAdjacencyList<TEdgeType> = {|
nodes: Uint32Array,
edges: Uint32Array,
edgeTypes: Uint8Array,
|};

// eslint-disable-next-line no-unused-vars
export type AdjacencyListOptions<TEdgeType> = {|
edgeCapacity?: number,
nodeCapacity?: number,
edgeTypes: Uint8Array,
|};

/** The upper bound above which capacity should be increased. */
Expand All @@ -40,6 +42,7 @@ const SHRINK_FACTOR = 0.5;
export default class AdjacencyList<TEdgeType: number = 1> {
#nodes /*: NodeTypeMap<TEdgeType | NullEdgeType> */;
#edges /*: EdgeTypeMap<TEdgeType | NullEdgeType> */;
#edgeTypes /*: Uint8Array */;

constructor(
opts?:
Expand All @@ -48,15 +51,18 @@ export default class AdjacencyList<TEdgeType: number = 1> {
) {
let nodes;
let edges;
let edgeTypes;

if (opts?.nodes) {
({nodes, edges} = opts);
({nodes, edges, edgeTypes} = opts);
this.#nodes = new NodeTypeMap(nodes);
this.#edges = new EdgeTypeMap(edges);
this.#edgeTypes = edgeTypes;
} else {
let {
nodeCapacity = NodeTypeMap.MIN_CAPACITY,
edgeCapacity = EdgeTypeMap.MIN_CAPACITY,
edgeTypes,
} = opts ?? {};
assert(
nodeCapacity <= NodeTypeMap.MAX_CAPACITY,
Expand All @@ -68,6 +74,7 @@ export default class AdjacencyList<TEdgeType: number = 1> {
);
this.#nodes = new NodeTypeMap(nodeCapacity);
this.#edges = new EdgeTypeMap(edgeCapacity);
this.#edgeTypes = edgeTypes;
}
}

Expand All @@ -87,6 +94,7 @@ export default class AdjacencyList<TEdgeType: number = 1> {
return {
nodes: this.#nodes.data,
edges: this.#edges.data,
edgeTypes: this.#edgeTypes,
};
}

Expand Down Expand Up @@ -195,6 +203,7 @@ export default class AdjacencyList<TEdgeType: number = 1> {
let copy = new AdjacencyList({
nodeCapacity: this.#nodes.capacity,
edgeCapacity: size,
edgeTypes: this.#edgeTypes,
});

// Copy the existing edges into the new array.
Expand Down Expand Up @@ -329,10 +338,33 @@ export default class AdjacencyList<TEdgeType: number = 1> {
hasEdge(
from: NodeId,
to: NodeId,
type: TEdgeType | NullEdgeType = 1,
type:
| AllEdgeTypes
| TEdgeType
| NullEdgeType
| Array<TEdgeType | NullEdgeType> = 1,
): boolean {
let hash = this.#edges.hash(from, to, type);
return this.#edges.addressOf(hash, from, to, type) !== null;
let hasEdge = (type: TEdgeType | NullEdgeType) => {
let hash = this.#edges.hash(from, to, type);
return this.#edges.addressOf(hash, from, to, type) !== null;
};

if (type === ALL_EDGE_TYPES || Array.isArray(type)) {
let types: Iterable<TEdgeType | NullEdgeType> =
// $FlowFixMe[incompatible-type-arg] this.edgeTypes will only contain valid edge types
Array.isArray(type) ? type : this.#edgeTypes;

for (let currType of types) {
if (hasEdge(currType)) {
return true;
}
}

return false;
}

// $FlowFixMe[incompatible-call] ALL_EDGE_TYPES has already been handled
return hasEdge(type);
}

/**
Expand Down Expand Up @@ -1076,7 +1108,7 @@ export class EdgeTypeMap<TEdgeType> extends SharedTypeMap<
hash: EdgeHash,
from: NodeId,
to: NodeId,
type: TEdgeType,
type: TEdgeType | NullEdgeType,
): EdgeAddress | null {
let address = this.head(hash);
while (address !== null) {
Expand Down
24 changes: 17 additions & 7 deletions packages/core/graph/src/ContentGraph.js
Expand Up @@ -6,12 +6,13 @@ import nullthrows from 'nullthrows';

export type ContentGraphOpts<TNode, TEdgeType: number = 1> = {|
...GraphOpts<TNode, TEdgeType>,
_contentKeyToNodeId: Map<ContentKey, NodeId>,
_nodeIdToContentKey: Map<NodeId, ContentKey>,
_contentKeyToNodeId?: Map<ContentKey, NodeId>,
_nodeIdToContentKey?: Map<NodeId, ContentKey>,
|};
export type SerializedContentGraph<TNode, TEdgeType: number = 1> = {|
...SerializedGraph<TNode, TEdgeType>,
_contentKeyToNodeId: Map<ContentKey, NodeId>,
_nodeIdToContentKey: Map<NodeId, ContentKey>,
|};

export default class ContentGraph<TNode, TEdgeType: number = 1> extends Graph<
Expand All @@ -23,27 +24,36 @@ export default class ContentGraph<TNode, TEdgeType: number = 1> extends Graph<

constructor(opts: ?ContentGraphOpts<TNode, TEdgeType>) {
if (opts) {
let {_contentKeyToNodeId, _nodeIdToContentKey, ...rest} = opts;
let {
_contentKeyToNodeId = new Map(),
_nodeIdToContentKey = new Map(),
...rest
} = opts;
super(rest);
this._contentKeyToNodeId = _contentKeyToNodeId;
this._nodeIdToContentKey = _nodeIdToContentKey;
} else {
super();
super(opts);
this._contentKeyToNodeId = new Map();
this._nodeIdToContentKey = new Map();
}
}

// $FlowFixMe[prop-missing]
static deserialize(
opts: ContentGraphOpts<TNode, TEdgeType>,
opts: SerializedContentGraph<TNode, TEdgeType>,
): ContentGraph<TNode, TEdgeType> {
return new ContentGraph(opts);
return new ContentGraph({
nodes: opts.nodes,
adjacencyList: opts.adjacencyList,
rootNodeId: opts.rootNodeId,
_contentKeyToNodeId: opts._contentKeyToNodeId,
_nodeIdToContentKey: opts._nodeIdToContentKey,
});
}

// $FlowFixMe[prop-missing]
serialize(): SerializedContentGraph<TNode, TEdgeType> {
// $FlowFixMe[prop-missing]
return {
...super.serialize(),
_contentKeyToNodeId: this._contentKeyToNodeId,
Expand Down
26 changes: 21 additions & 5 deletions packages/core/graph/src/Graph.js
Expand Up @@ -13,6 +13,7 @@ export type GraphOpts<TNode, TEdgeType: number = 1> = {|
nodes?: Map<NodeId, TNode>,
adjacencyList?: SerializedAdjacencyList<TEdgeType>,
rootNodeId?: ?NodeId,
edgeTypes?: {[key: string]: TEdgeType},
|};

export type SerializedGraph<TNode, TEdgeType: number = 1> = {|
Expand All @@ -24,6 +25,10 @@ export type SerializedGraph<TNode, TEdgeType: number = 1> = {|
export type AllEdgeTypes = -1;
export const ALL_EDGE_TYPES: AllEdgeTypes = -1;

function objectValues<Key, Value>(obj: {[Key]: Value}): Array<Value> {
return Object.keys(obj).map(key => obj[key]);
}

export default class Graph<TNode, TEdgeType: number = 1> {
nodes: Map<NodeId, TNode>;
adjacencyList: AdjacencyList<TEdgeType>;
Expand All @@ -34,17 +39,24 @@ export default class Graph<TNode, TEdgeType: number = 1> {
this.setRootNodeId(opts?.rootNodeId);

let adjacencyList = opts?.adjacencyList;
this.adjacencyList = adjacencyList
? AdjacencyList.deserialize(adjacencyList)
: new AdjacencyList<TEdgeType>();

if (adjacencyList) {
this.adjacencyList = AdjacencyList.deserialize(adjacencyList);
} else {
// default edgeTypes to array containing just the null edge type
let edgeTypes = new Uint8Array(
opts?.edgeTypes ? objectValues(opts.edgeTypes) : [1],
mattcompiles marked this conversation as resolved.
Show resolved Hide resolved
);
this.adjacencyList = new AdjacencyList<TEdgeType>({edgeTypes});
}
}

setRootNodeId(id: ?NodeId) {
this.rootNodeId = id;
}

static deserialize(
opts: GraphOpts<TNode, TEdgeType>,
opts: SerializedGraph<TNode, TEdgeType>,
): Graph<TNode, TEdgeType> {
return new this({
nodes: opts.nodes,
Expand Down Expand Up @@ -104,7 +116,11 @@ export default class Graph<TNode, TEdgeType: number = 1> {
hasEdge(
from: NodeId,
to: NodeId,
type?: TEdgeType | NullEdgeType = 1,
type?:
| TEdgeType
| NullEdgeType
| Array<TEdgeType | NullEdgeType>
| AllEdgeTypes = 1,
): boolean {
return this.adjacencyList.hasEdge(from, to, type);
}
Expand Down