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

[Fiber] Transfer everything from Element onto the Fiber and use Tag instead of Stage #6903

Merged
merged 2 commits into from
May 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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