Skip to content

Commit

Permalink
Merge a68d696 into 6000550
Browse files Browse the repository at this point in the history
  • Loading branch information
FrancoisChabot committed Apr 23, 2021
2 parents 6000550 + a68d696 commit 64cad89
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 84 deletions.
9 changes: 2 additions & 7 deletions src/Environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ProbingContext } from './ApiTypes';
export type DisposeOp = () => void;
export interface Environment {
_onDispose: (op: DisposeOp) => void;
_getProbingContext: () => ProbingContext | undefined;
_getProbingContext: () => ProbingContext;
}

let _currentEnv: Environment | null = null;
Expand Down Expand Up @@ -50,10 +50,5 @@ export function useProbingContext(): ProbingContext {
throw new Error('Environment underflow');
}

const result = _currentEnv!._getProbingContext();
if (process.env.NODE_ENV !== 'production' && !result) {
throw new Error('There is no active probing context');
}

return result!;
return _currentEnv!._getProbingContext();
}
21 changes: 14 additions & 7 deletions src/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export interface NodeBuildData {
_cb: (...arg: unknown[]) => unknown;
_args: unknown[];

_next?: IPNode;
_resolveAs?: IPNode;
_next?: BaseNode;
_resolveAs: BaseNode;
_prober: IProber;
_context: ProbingContext;
}
Expand All @@ -43,17 +43,24 @@ export abstract class BaseNode implements IPNode {
}

dispose(): void {
if (this._onDispose) {
this._onDispose.forEach((c) => c());
this._onDispose = undefined;
}
// Nodes should only ever be disposed once
this._onDispose.forEach((c) => c());
}

_addToDispose(ops: DisposeOp[]): void {
this._onDispose.push(...ops);
}

_assign(rhs: BaseNode): void {
this._result = rhs._result;
this._onDispose.push(...rhs._onDispose);
}

abstract get result(): unknown;

_result?: unknown;
_probed_pnodetype?: number;
_onDispose?: DisposeOp[];
_onDispose: DisposeOp[] = [];

_buildData?: NodeBuildData;
_uniqueNodeId?: number;
Expand Down
94 changes: 36 additions & 58 deletions src/Prober.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ const isIntrinsic = <I>(cb: IKeys<I> | ((...args: unknown[]) => unknown)): cb is
return typeof cb === 'string';
};

const addToDisposeQueue = (node: BaseNode, ops: DisposeOp[]) => {
if (!node._onDispose) {
node._onDispose = ops;
} else {
node._onDispose = node._onDispose.concat(ops);
}
};

let _NextUniqueNodeId = 0;

interface NodeQueue {
Expand All @@ -45,14 +37,14 @@ class Prober<I extends FuncMap> implements IProber {
private _intrinsics: Partial<I>;
private _fallback?: IntrinsicFallback<I>;
private _stack: ProberStackFrame[] = [];
private _current: ProberStackFrame = { _disposeOps: [] };
private _currentFrame: ProberStackFrame = { _disposeOps: [] };

_onDispose(op: DisposeOp): void {
this._current._disposeOps.push(op);
this._currentFrame._disposeOps.push(op);
}

_getProbingContext(): ProbingContext | undefined {
return this._current!._node!._buildData!._context;
_getProbingContext(): ProbingContext {
return this._currentFrame!._node!._buildData!._context;
}

constructor(intrinsics: Partial<I>, fallback?: IntrinsicFallback<I>) {
Expand All @@ -79,14 +71,15 @@ class Prober<I extends FuncMap> implements IProber {
newNode._uniqueNodeId = _NextUniqueNodeId++;
}

if (!this._current._announced) {
this._current._announced = { _head: newNode, _tail: newNode };
if (!this._currentFrame._announced) {
this._currentFrame._announced = { _head: newNode, _tail: newNode };
} else {
this._current._announced._tail._buildData!._next = newNode;
this._current._announced._tail = newNode;
this._currentFrame._announced._tail._buildData!._next = newNode;
this._currentFrame._announced._tail = newNode;
}

newNode._buildData = {
_resolveAs: newNode,
_cb,
_prober: this,
_args,
Expand All @@ -100,41 +93,33 @@ class Prober<I extends FuncMap> implements IProber {

_finalizeNode(node: BaseNode) {
// If a component returns a Node (as opposed to a value), then we short-circuit to the parent.
let destinationNode = node;
while (destinationNode._buildData && destinationNode._buildData._resolveAs) {
destinationNode = destinationNode._buildData!._resolveAs;
}
const bd = node._buildData!;
const destinationNode = bd._resolveAs;

this._current._node = node;
const { _cb, _args } = node._buildData!;
const cbResult = _cb(..._args, node._buildData!._context);
this._currentFrame._node = node;
const cbResult = bd._cb(...bd._args, bd._context);

if (isPNode(cbResult)) {
if (cbResult.finalized) {
// Post-ex-facto proxying.
destinationNode._result = cbResult._result;
if (cbResult._onDispose) {
addToDisposeQueue(destinationNode, cbResult._onDispose);
cbResult._onDispose = [];
}
destinationNode._assign(cbResult);
} else {
cbResult._buildData!._resolveAs = destinationNode;
}
} else {
destinationNode._result = cbResult;
}

if (this._current._disposeOps.length > 0) {
addToDisposeQueue(destinationNode, this._current._disposeOps);
this._current._disposeOps = [];
if (this._currentFrame._disposeOps.length > 0) {
destinationNode._addToDispose(this._currentFrame._disposeOps);
this._currentFrame._disposeOps = [];
}
}

_finalize(target: IPNode): void {
if (process.env.NODE_ENV === 'check') {
let lookup: BaseNode | undefined;
if (this._current._announced) {
lookup = this._current._announced._head;
if (this._currentFrame._announced) {
lookup = this._currentFrame._announced._head;
}
while (lookup && lookup !== target) {
lookup = lookup._buildData!._next;
Expand All @@ -144,58 +129,51 @@ class Prober<I extends FuncMap> implements IProber {
}
}

let node: BaseNode = this._current._announced!._head!;
let end: BaseNode = target as BaseNode;
let node = this._currentFrame._announced!._head!;
let end = target as BaseNode;

if (end._buildData!._next) {
this._current._announced!._head = end._buildData!._next;
this._currentFrame._announced!._head = end._buildData!._next;
} else {
this._current._announced = undefined;
this._currentFrame._announced = undefined;
}
end._buildData!._next = undefined;

/*
//These two steps are, I suspect, Technically unnecessary
if (!this._current._announced._head) {
this._current._announced = {};
}
*/
pushEnv(this);
this._stack.push(this._current);
this._current = { _node: node, _disposeOps: [] };
this._stack.push(this._currentFrame);
this._currentFrame = { _node: node, _disposeOps: [] };

let done = false;
while (!done) {
this._finalizeNode(node);

// Queue up any work that was discovered in the process.
if (this._current._announced) {
end = this._current._announced._tail;
node._buildData!._next = this._current._announced._head;
this._current._announced = undefined;
// Queue up any work that was discovered in the process,
// and update our end target so that we complete it before
// returning.
if (this._currentFrame._announced) {
end = this._currentFrame._announced._tail;
node._buildData!._next = this._currentFrame._announced._head;
this._currentFrame._announced = undefined;
}

done = node === end;
const nextNode = node._buildData!._next as BaseNode;
const nextNode = node._buildData!._next;
node._buildData = undefined;
node = nextNode;
node = nextNode!;
}

this._current = this._stack.pop()!;
this._currentFrame = this._stack.pop()!;
popEnv();
}

private _getCb<T extends IKeys<I> | ComponentCb>(what: T): { _cb: ComponentCb; _name: string } {
if (isIntrinsic<I>(what)) {
let _cb: ComponentCb | undefined = this._intrinsics[what];
const _name = what.toString();
if (!_cb) {
// This is safe, it's caught at the start of _announce()
_cb = this._fallback!;
}

return { _cb: _cb!, _name };
return { _cb: _cb!, _name: what.toString() };
} else {
return { _cb: what as ComponentCb, _name: (what as ComponentCb).name };
}
Expand Down
2 changes: 1 addition & 1 deletion tests/dynamicList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const cleanup = () => {
beforeEach(() => {
pushEnv({
_onDispose: (op: DisposeOp) => disposeQueue.push(op),
_getProbingContext: () => undefined,
_getProbingContext: () => ({ componentName: '' }),
});
});

Expand Down
2 changes: 1 addition & 1 deletion tests/dynamicOperations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const cleanup = () => {
beforeEach(() => {
pushEnv({
_onDispose: (op: DisposeOp) => disposeQueue.push(op),
_getProbingContext: () => undefined,
_getProbingContext: () => ({ componentName: '' }),
});
});

Expand Down
2 changes: 1 addition & 1 deletion tests/dynamicVal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const cleanup = () => {
beforeEach(() => {
pushEnv({
_onDispose: (op: DisposeOp) => disposeQueue.push(op),
_getProbingContext: () => undefined,
_getProbingContext: () => ({ componentName: '' }),
});
});

Expand Down
11 changes: 2 additions & 9 deletions tests/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ describe('useOnDispose ', () => {

class TestEnv implements Environment {
count = 0;
probeContext?: ProbingContext;
probeContext: ProbingContext = { componentName: '' };
_onDispose(): void {
this.count += 1;
}

_getProbingContext(): ProbingContext | undefined {
_getProbingContext(): ProbingContext {
return this.probeContext;
}
}
Expand Down Expand Up @@ -77,13 +77,6 @@ describe('useProbingContext ', () => {
expectThrowInNotProd(useProbingContext);
});

it('Fails if probing context is set', () => {
const env = new TestEnv();
pushEnv(env);
expectThrowInNotProd(useProbingContext);
popEnv();
});

it('Works if a probing context is set', () => {
const env = new TestEnv();
env.probeContext = { componentName: 'aaa' };
Expand Down

0 comments on commit 64cad89

Please sign in to comment.