Skip to content

Commit

Permalink
Merge pull request #6903 from sebmarkbage/newreconciler
Browse files Browse the repository at this point in the history
[Fiber] Transfer everything from Element onto the Fiber and use Tag instead of Stage
  • Loading branch information
sebmarkbage committed May 31, 2016
2 parents 4e82e8b + 811084d commit 7de2375
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 79 deletions.
46 changes: 27 additions & 19 deletions src/renderers/shared/fiber/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,22 @@ export type Fiber = {
child: ?Fiber,
sibling: ?Fiber,

// Input is the data coming into process this fiber. Arguments.
// Unique identifier of this child.
key: ?string,

// The function/class/module associated with this fiber.
type: any,

// The ref last used to attach this node.
// I'll avoid adding an owner field for prod and model that as functions.
ref: null | (handle : ?Object) => void,

// Input is the data coming into process this fiber. Arguments. Props.
input: any, // This type will be more specific once we overload the tag.
// Output is the return value of this fiber, or a linked list of return values
// if this returns multiple values. Such as a fragment.
output: any, // This type will be more specific once we overload the tag.

// Used by multi-stage coroutines.
stage: number, // Consider reusing the tag field instead.

// This will be used to quickly determine if a subtree has no pending changes.
hasPendingChanges: bool,

Expand All @@ -52,7 +59,7 @@ export type Fiber = {

};

var createFiber = function(tag : number) : Fiber {
var createFiber = function(tag : number, key : null | string) : Fiber {
return {

tag: tag,
Expand All @@ -61,11 +68,13 @@ var createFiber = function(tag : number) : Fiber {
child: null,
sibling: null,

key: key,
type: null,
ref: null,

input: null,
output: null,

stage: 0,

hasPendingChanges: true,

stateNode: null,
Expand All @@ -78,23 +87,21 @@ function shouldConstruct(Component) {
}

exports.createFiberFromElement = function(element : ReactElement) {
const fiber = exports.createFiberFromElementType(element.type);
if (typeof element.type === 'object') {
// Hacky McHack
element = ReactElement(fiber.input, null, element.ref, null, null, null, element.props);
}
fiber.input = element;
const fiber = exports.createFiberFromElementType(element.type, element.key);
fiber.input = element.props;
return fiber;
};

exports.createFiberFromElementType = function(type : mixed) {
exports.createFiberFromElementType = function(type : mixed, key : null | string) {
let fiber;
if (typeof type === 'function') {
fiber = shouldConstruct(type) ?
createFiber(ClassComponent) :
createFiber(IndeterminateComponent);
createFiber(ClassComponent, key) :
createFiber(IndeterminateComponent, key);
fiber.type = type;
} else if (typeof type === 'string') {
fiber = createFiber(HostComponent);
fiber = createFiber(HostComponent, key);
fiber.type = type;
} else if (typeof type === 'object' && type !== null) {
// Currently assumed to be a continuation and therefore is a fiber already.
fiber = type;
Expand All @@ -105,12 +112,13 @@ exports.createFiberFromElementType = function(type : mixed) {
};

exports.createFiberFromCoroutine = function(coroutine : ReactCoroutine) {
const fiber = createFiber(CoroutineComponent);
const fiber = createFiber(CoroutineComponent, coroutine.key);
fiber.type = coroutine.handler;
fiber.input = coroutine;
return fiber;
};

exports.createFiberFromYield = function(yieldNode : ReactYield) {
const fiber = createFiber(YieldComponent);
const fiber = createFiber(YieldComponent, yieldNode.key);
return fiber;
};
32 changes: 12 additions & 20 deletions src/renderers/shared/fiber/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,13 @@ var {
ClassComponent,
HostComponent,
CoroutineComponent,
CoroutineHandlerPhase,
YieldComponent,
} = ReactTypesOfWork;

function getElement(unitOfWork) : ReactElement {
var element = unitOfWork.input;
if (!element) {
throw new Error('Should be resolved by now');
}
return (element : ReactElement);
}

function updateFunctionalComponent(unitOfWork) {
var element = getElement(unitOfWork);
var fn = element.type;
var props = element.props;
var fn = unitOfWork.type;
var props = unitOfWork.input;
console.log('perform work on:', fn.name);
var nextChildren = fn(props);

Expand All @@ -49,10 +41,9 @@ function updateFunctionalComponent(unitOfWork) {
}

function updateHostComponent(unitOfWork) {
var element = getElement(unitOfWork);
console.log('host component', element.type, typeof element.props.children === 'string' ? element.props.children : '');
console.log('host component', unitOfWork.type, typeof unitOfWork.input.children === 'string' ? unitOfWork.input.children : '');

var nextChildren = element.props.children;
var nextChildren = unitOfWork.input.children;
unitOfWork.child = ReactChildFiber.reconcileChildFibers(
unitOfWork,
unitOfWork.child,
Expand All @@ -61,9 +52,8 @@ function updateHostComponent(unitOfWork) {
}

function mountIndeterminateComponent(unitOfWork) {
var element = getElement(unitOfWork);
var fn = element.type;
var props = element.props;
var fn = unitOfWork.type;
var props = unitOfWork.input;
var value = fn(props);
if (typeof value === 'object' && value && typeof value.render === 'function') {
console.log('performed work on class:', fn.name);
Expand All @@ -86,7 +76,7 @@ function updateCoroutineComponent(unitOfWork) {
if (!coroutine) {
throw new Error('Should be resolved by now');
}
console.log('begin coroutine', coroutine.handler.name);
console.log('begin coroutine', unitOfWork.type.name);
unitOfWork.child = ReactChildFiber.reconcileChildFibers(
unitOfWork,
unitOfWork.child,
Expand All @@ -108,9 +98,11 @@ function beginWork(unitOfWork : Fiber) : ?Fiber {
case HostComponent:
updateHostComponent(unitOfWork);
break;
case CoroutineHandlerPhase:
// This is a restart. Reset the tag to the initial phase.
unitOfWork.tag = CoroutineComponent;
// Intentionally fall through since this is now the same.
case CoroutineComponent:
// Reset the stage to zero.
unitOfWork.stage = 0;
updateCoroutineComponent(unitOfWork);
// This doesn't take arbitrary time so we could synchronously just begin
// eagerly do the work of unitOfWork.child as an optimization.
Expand Down
68 changes: 34 additions & 34 deletions src/renderers/shared/fiber/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var {
ClassComponent,
HostComponent,
CoroutineComponent,
CoroutineHandlerPhase,
YieldComponent,
} = ReactTypesOfWork;

Expand Down Expand Up @@ -52,61 +53,60 @@ function recursivelyFillYields(yields, output : ?Fiber | ?ReifiedYield) {
}
}

function handleCoroutine(unitOfWork : Fiber) {
function moveCoroutineToHandlerPhase(unitOfWork : Fiber) {
var coroutine = (unitOfWork.input : ?ReactCoroutine);
if (!coroutine) {
throw new Error('Should be resolved by now');
}

if (unitOfWork.stage === 0) {
// First step of the coroutine has completed. Now we need to do the second.
// TODO: It would be nice to have a multi stage coroutine represented by a
// single component, or at least tail call optimize nested ones.
// TODO: If we end up not using multi stage coroutines, we could also reuse
// the tag field to switch between the two stages.
unitOfWork.stage = 1;
// First step of the coroutine has completed. Now we need to do the second.
// TODO: It would be nice to have a multi stage coroutine represented by a
// single component, or at least tail call optimize nested ones. Currently
// that requires additional fields that we don't want to add to the fiber.
// So this requires nested handlers.
unitOfWork.tag = CoroutineHandlerPhase;

// Build up the yields.
// TODO: Compare this to a generator or opaque helpers like Children.
var yields : Array<ReifiedYield> = [];
var child = unitOfWork.child;
while (child) {
recursivelyFillYields(yields, child.output);
child = child.sibling;
}
var fn = coroutine.handler;
var props = coroutine.props;
var nextChildren = fn(props, yields);

unitOfWork.stateNode = ReactChildFiber.reconcileChildFibers(
unitOfWork,
unitOfWork.stateNode,
nextChildren
);
return unitOfWork.stateNode;
} else {
// The coroutine is now complete.
transferOutput(unitOfWork.stateNode, unitOfWork);
return null;
// Build up the yields.
// TODO: Compare this to a generator or opaque helpers like Children.
var yields : Array<ReifiedYield> = [];
var child = unitOfWork.child;
while (child) {
recursivelyFillYields(yields, child.output);
child = child.sibling;
}
var fn = coroutine.handler;
var props = coroutine.props;
var nextChildren = fn(props, yields);

unitOfWork.stateNode = ReactChildFiber.reconcileChildFibers(
unitOfWork,
unitOfWork.stateNode,
nextChildren
);
return unitOfWork.stateNode;
}

exports.completeWork = function(unitOfWork : Fiber) : ?Fiber {
switch (unitOfWork.tag) {
case FunctionalComponent:
console.log('/functional component', unitOfWork.input.type.name);
console.log('/functional component', unitOfWork.type.name);
transferOutput(unitOfWork.child, unitOfWork);
break;
case ClassComponent:
console.log('/class component', unitOfWork.input.type.name);
console.log('/class component', unitOfWork.type.name);
transferOutput(unitOfWork.child, unitOfWork);
break;
case HostComponent:
console.log('/host component', unitOfWork.input.type);
console.log('/host component', unitOfWork.type);
break;
case CoroutineComponent:
console.log('/coroutine component', unitOfWork.input.handler.name);
return handleCoroutine(unitOfWork);
return moveCoroutineToHandlerPhase(unitOfWork);
case CoroutineHandlerPhase:
transferOutput(unitOfWork.stateNode, unitOfWork);
// Reset the tag to now be a first phase coroutine.
unitOfWork.tag = CoroutineComponent;
break;
case YieldComponent:
// Does nothing.
break;
Expand Down
7 changes: 4 additions & 3 deletions src/renderers/shared/fiber/ReactReifiedYield.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ var ReactFiber = require('ReactFiber');
export type ReifiedYield = { continuation: Fiber, props: Object };

exports.createReifiedYield = function(yieldNode : ReactYield) : ReifiedYield {
var fiber = ReactFiber.createFiberFromElementType(yieldNode.continuation);
// Hacky way to store the continuation
fiber.input = yieldNode.continuation;
var fiber = ReactFiber.createFiberFromElementType(
yieldNode.continuation,
yieldNode.key
);
return {
continuation: fiber,
props: yieldNode.props,
Expand Down
3 changes: 2 additions & 1 deletion src/renderers/shared/fiber/ReactTypesOfWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ var TypesOfWork = {
ClassComponent: 2,
HostComponent: 3,
CoroutineComponent: 4,
YieldComponent: 5,
CoroutineHandlerPhase: 5,
YieldComponent: 6,
};

module.exports = TypesOfWork;
4 changes: 2 additions & 2 deletions src/renderers/shared/fiber/isomorphic/ReactCoroutine.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ type CoroutineHandler<T> = (props: T, yields: Array<ReifiedYield>) => ReactNodeL

export type ReactCoroutine = {
$$typeof: Symbol | number,
key: ?string,
key: null | string,
children: any,
// This should be a more specific CoroutineHandler
handler: (props: any, yields: Array<ReifiedYield>) => ReactNodeList,
props: mixed,
};
export type ReactYield = {
$$typeof: Symbol | number,
key: ?string,
key: null | string,
props: Object,
continuation: mixed
};
Expand Down

0 comments on commit 7de2375

Please sign in to comment.