Removes Flow dependency from the React compiler

Summary:
Release notes: none

We no longer need Flow with the recent purity changes and this also makes the tests require less dependencies. Other small refactors were made to make way for removal of the Flow code.
Closes https://github.com/facebook/prepack/pull/1345

Differential Revision: D6795670

Pulled By: trueadm

fbshipit-source-id: ddd278b2054eaf8465e4ccc1acd014e96e343968
This commit is contained in:
Dominic Gannaway 2018-01-24 04:45:33 -08:00 committed by Facebook Github Bot
parent f572632234
commit 577694ceb7
22 changed files with 58 additions and 335 deletions

View File

@ -153,10 +153,6 @@ export function ClassDefinitionEvaluation(
// Provide a hint that this prototype is that of a class
proto.$IsClassPrototype = true;
// react. Check the Flow class paramater annotations, stored in "superTypeParameters"
if (realm.react.enabled && realm.react.flowRequired && ast.superTypeParameters) {
proto.$SuperTypeParameters = ast.superTypeParameters;
}
let constructor;
let emptyConstructor = false;
let ClassBody: Array<BabelNodeClassMethod> = [];

View File

@ -1,127 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
import { Realm } from "../realm.js";
import buildExpressionTemplate from "../utils/builder.js";
import { Create } from "../singletons.js";
import { ValuesDomain } from "../domains/index.js";
import { Value, AbstractValue, ObjectValue, ArrayValue, AbstractObjectValue } from "../values/index.js";
import invariant from "../invariant.js";
import { type ObjectTypeTemplate } from "./utils.js";
export function createObject(realm: Realm, shape: ObjectTypeTemplate | null, name: string | null): ObjectValue {
let obj = Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype);
if (shape != null) {
// to get around Flow complaining that shape could be null
let shapeThatIsNotNull = shape;
Object.keys(shape).forEach((id: string) => {
let value = shapeThatIsNotNull[id];
invariant(value instanceof Value, "creation of object failed due to object containing non-value properties");
obj.$Set(id, value, obj);
if (name !== null) {
value.intrinsicName = `${name}.${id}`;
}
});
}
if (name !== null) {
obj.intrinsicName = name;
}
return obj;
}
export function createArray(realm: Realm, name: string | null): ArrayValue {
let obj = Create.ArrayCreate(realm, 0, realm.intrinsics.ArrayPrototype);
if (name !== null) {
obj.intrinsicName = name;
}
return ((obj: any): ArrayValue);
}
function _createAbstractArray(realm: Realm, name: string): AbstractValue {
let value = AbstractValue.createFromTemplate(realm, buildExpressionTemplate(name), ArrayValue, [], name);
value.intrinsicName = name;
let template = createArray(realm, name);
template.makePartial();
template.makeSimple();
value.values = new ValuesDomain(new Set([template]));
realm.rebuildNestedProperties(value, name);
return value;
}
export function createAbstractObject(
realm: Realm,
name: string | null,
objectTypes: ObjectTypeTemplate | null,
template?: ObjectValue
): AbstractObjectValue {
if (name === null) {
name = "unknown";
}
let value = AbstractValue.createFromTemplate(realm, buildExpressionTemplate(name), ObjectValue, [], name);
value.intrinsicName = name;
if (template === undefined) {
template = createObject(realm, objectTypes, name);
}
template.makePartial();
template.makeSimple();
value.values = new ValuesDomain(new Set([template]));
realm.rebuildNestedProperties(value, name);
invariant(value instanceof AbstractObjectValue);
return value;
}
export function createAbstractObjectFromFlowTypes(
realm: Realm,
name: string | null,
objectTypes: ObjectTypeTemplate | null | string
): AbstractObjectValue {
if (typeof objectTypes === "string") {
invariant(
objectTypes === "empty" || objectTypes === "object",
`Expected an object or a string of "empty" or "object" for createAbstractObject() paramater "objectTypes"`
);
return createAbstractObject(realm, name, null);
}
if (objectTypes !== null) {
let propTypeObject = {};
let objTypes = objectTypes;
invariant(objTypes);
Object.keys(objTypes).forEach(key => {
let value = objTypes[key];
let propertyName = name !== null ? `${name}.${key}` : key;
if (typeof value === "string") {
if (value === "array") {
propTypeObject[key] = _createAbstractArray(realm, propertyName);
} else if (value === "object") {
propTypeObject[key] = createAbstractObject(realm, propertyName, null);
} else {
propTypeObject[key] = createAbstractByType(realm, value, propertyName);
}
} else if (typeof value === "object" && value !== null) {
propTypeObject[key] = createAbstractObject(realm, propertyName, value);
} else {
invariant(false, `Unknown propType value of "${value}" for "${key}"`);
}
});
return createAbstractObject(realm, name, propTypeObject);
} else {
return createAbstractObject(realm, name, null);
}
}
export function createAbstractByType(realm: Realm, typeNameString: string, name: string): Value {
let type = Value.getTypeFromName(typeNameString);
invariant(type !== undefined, "createAbstractByType() cannot be undefined");
let value = AbstractValue.createFromTemplate(realm, buildExpressionTemplate(name), type, [], name);
value.intrinsicName = name;
return value;
}

View File

@ -1,141 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/* @flow */
import type { typeAnnotation } from "babel-types";
import invariant from "../invariant.js";
import traverse from "babel-traverse";
import { BabelNode } from "babel-types";
import * as t from "babel-types";
export type ObjectTypeTemplate = {
[key: string]: string | ObjectTypeTemplate,
};
export function flowAnnotationToObjectTypeTemplate(annotation: typeAnnotation): string | ObjectTypeTemplate {
if (annotation.type === "TypeAnnotation") {
return flowAnnotationToObjectTypeTemplate(annotation.typeAnnotation);
} else if (annotation.type === "GenericTypeAnnotation") {
if (annotation.id.type === "Identifier") {
let identifier = annotation.id.name;
switch (identifier) {
case "Function":
return "function";
case "Object":
return "object";
case "Array":
return "array";
case "any":
case "empty":
return "empty";
default:
// get the Flow type
invariant(false, "Flow types are currently not supported");
}
} else {
invariant(false, "unknown generic Flow type annotation node");
}
} else if (annotation.type === "EmptyTypeAnnotation") {
return "empty";
} else if (annotation.type === "BooleanTypeAnnotation") {
return "boolean";
} else if (annotation.type === "StringTypeAnnotation") {
return "string";
} else if (annotation.type === "NumberTypeAnnotation") {
return "number";
} else if (annotation.type === "FunctionTypeAnnotation") {
return "function";
} else if (annotation.type === "ArrayTypeAnnotation") {
return "array";
} else if (annotation.type === "ObjectTypeAnnotation") {
let obj = {};
annotation.properties.forEach(property => {
if (property.type === "ObjectTypeProperty") {
if (property.key.type === "Identifier") {
obj[property.key.name] = flowAnnotationToObjectTypeTemplate(property.value);
} else {
invariant(false, "only Identifier nodes are supported in ObjectTypeProperty keys");
}
} else {
invariant(false, "only ObjectTypeProperty properties are supported in ObjectTypeAnnotation");
}
});
return obj;
} else if (annotation.type === "AnyTypeAnnotation") {
return "empty";
} else {
invariant(false, "unknown Flow type annotation node");
}
}
// Taken directly from Babel:
// https://github.com/babel/babel/blob/cde005422701a69ff21044c138c29a5ad23b6d0a/packages/babel-plugin-transform-flow-strip-types/src/index.js#L32-L107
// Copyright 2015-present Sebastian McKenzie / Babel project (https://github.com/babel)
// only the lines reflected in the above were used
export function stripFlowTypeAnnotations(ast: BabelNode): void {
traverse(
ast,
{
ImportDeclaration(path) {
if (!path.node.specifiers.length) return;
let typeCount = 0;
path.node.specifiers.forEach(({ importKind }) => {
if (importKind === "type" || importKind === "typeof") {
typeCount++;
}
});
if (typeCount === path.node.specifiers.length) {
path.remove();
}
},
Flow(path) {
path.remove();
},
ClassProperty(path) {
path.node.variance = null;
path.node.typeAnnotation = null;
if (!path.node.value) path.remove();
},
Class(path) {
path.node.implements = null;
path.get("body.body").forEach(child => {
if (child.isClassProperty()) {
child.node.typeAnnotation = null;
if (!child.node.value) child.remove();
}
});
},
AssignmentPattern({ node }) {
node.left.optional = false;
},
Function({ node }) {
for (let i = 0; i < node.params.length; i++) {
const param = node.params[i];
param.optional = false;
if (param.type === "AssignmentPattern") {
param.left.optional = false;
}
}
node.predicate = null;
},
TypeCastExpression(path) {
let { node } = path;
do {
node = node.expression;
} while (t.isTypeCastExpression(node));
path.replaceWith(node);
},
},
undefined,
(undefined: any),
undefined
);
}

View File

@ -13,14 +13,13 @@ import type { Realm } from "../../realm.js";
import { AbstractValue, NativeFunctionValue, Value, StringValue } from "../../values/index.js";
import buildExpressionTemplate from "../../utils/builder.js";
import { createMockReact } from "./mocks.js";
import { createAbstractObject } from "../../flow/abstractObjectFactories.js";
import invariant from "../../invariant";
export default function(realm: Realm): void {
let global = realm.$GlobalObject;
// module.exports support
let moduleValue = createAbstractObject(realm, "module", null);
let moduleValue = AbstractValue.createAbstractObject(realm, "module");
global.$DefineOwnProperty("module", {
value: moduleValue,
writable: true,

View File

@ -11,10 +11,8 @@
import { Realm } from "../realm.js";
import { ECMAScriptSourceFunctionValue, AbstractValue, ObjectValue, AbstractObjectValue } from "../values/index.js";
import { flowAnnotationToObjectTypeTemplate } from "../flow/utils.js";
import * as t from "babel-types";
import type { BabelNodeIdentifier } from "babel-types";
import { createAbstractObject, createAbstractObjectFromFlowTypes } from "../flow/abstractObjectFactories.js";
import { valueIsClassComponent } from "./utils";
import { ExpectedBailOut, SimpleClassBailOut } from "./errors.js";
import { Get } from "../methods/index.js";
@ -31,22 +29,10 @@ const lifecycleMethods = new Set([
"componentWillReceiveProps",
]);
export function getInitialProps(
realm: Realm,
componentType: ECMAScriptSourceFunctionValue
): ObjectValue | AbstractObjectValue {
export function getInitialProps(realm: Realm, componentType: ECMAScriptSourceFunctionValue): AbstractObjectValue {
let propsName = null;
let propTypes = null;
if (valueIsClassComponent(realm, componentType)) {
propsName = "this.props";
// if flow is not required, do not try to construct the object from Flow types
if (realm.react.flowRequired) {
// it's a class component, so we need to check the type on for props of the component prototype
let superTypeParameters = componentType.$SuperTypeParameters;
if (superTypeParameters !== undefined) {
throw new ExpectedBailOut("props on class components not yet supported");
}
}
} else {
// otherwise it's a functional component, where the first paramater of the function is "props" (if it exists)
if (componentType.$FormalParameters.length > 0) {
@ -54,26 +40,15 @@ export function getInitialProps(
if (t.isIdentifier(firstParam)) {
propsName = ((firstParam: any): BabelNodeIdentifier).name;
}
// if flow is not required, do not try to construct the object from Flow types
if (realm.react.flowRequired) {
let propsTypeAnnotation = firstParam.typeAnnotation !== undefined && firstParam.typeAnnotation;
// we expect that if there's a props paramater, it should always have Flow annotations
if (!propsTypeAnnotation) {
throw new ExpectedBailOut(`root component missing Flow type annotations for the "props" paramater`);
}
propTypes = flowAnnotationToObjectTypeTemplate(propsTypeAnnotation);
}
}
}
return createAbstractObjectFromFlowTypes(realm, propsName, propTypes);
let value = AbstractValue.createAbstractObject(realm, propsName || "props");
invariant(value instanceof AbstractObjectValue);
return value;
}
export function getInitialContext(
realm: Realm,
componentType: ECMAScriptSourceFunctionValue
): ObjectValue | AbstractObjectValue {
export function getInitialContext(realm: Realm, componentType: ECMAScriptSourceFunctionValue): AbstractObjectValue {
let contextName = null;
let contextTypes = null;
if (valueIsClassComponent(realm, componentType)) {
// it's a class component, so we need to check the type on for context of the component prototype
let superTypeParameters = componentType.$SuperTypeParameters;
@ -89,15 +64,10 @@ export function getInitialContext(
if (t.isIdentifier(secondParam)) {
contextName = ((secondParam: any): BabelNodeIdentifier).name;
}
let contextTypeAnnotation = secondParam.typeAnnotation !== undefined && secondParam.typeAnnotation;
// we expect that if there's a context param, it should always have Flow annotations
if (!contextTypeAnnotation) {
throw new ExpectedBailOut(`root component missing Flow type annotations for the "context" paramater`);
}
contextTypes = flowAnnotationToObjectTypeTemplate(contextTypeAnnotation);
}
}
return createAbstractObjectFromFlowTypes(realm, contextName, contextTypes);
let value = AbstractValue.createAbstractObject(realm, contextName || "context");
return value;
}
export function createSimpleClassInstance(
@ -156,9 +126,9 @@ export function createClassInstance(
}
}
// assign state
Properties.Set(realm, instance, "state", createAbstractObject(realm, "this.state", null), true);
Properties.Set(realm, instance, "state", AbstractValue.createAbstractObject(realm, "this.state"), true);
// assign refs
Properties.Set(realm, instance, "refs", createAbstractObject(realm, "this.refs", null), true);
Properties.Set(realm, instance, "refs", AbstractValue.createAbstractObject(realm, "this.refs"), true);
// assign props
Properties.Set(realm, instance, "props", props, true);
// assign context
@ -166,5 +136,7 @@ export function createClassInstance(
// enable serialization to support simple instance variables properties
instance.refuseSerialization = false;
// return the instance in an abstract object
return createAbstractObject(realm, "this", null, instance);
let value = AbstractValue.createAbstractObject(realm, "this", instance);
invariant(value instanceof AbstractObjectValue);
return value;
}

View File

@ -254,3 +254,13 @@ export function convertSimpleClassComponentToFunctionalComponent(
);
});
}
export function normalizeFunctionalComponentParamaters(func: ECMAScriptSourceFunctionValue): void {
func.$FormalParameters = func.$FormalParameters.map((param, i) => {
if (i === 0) {
return t.identifier("props");
} else {
return t.identifier("context");
}
});
}

View File

@ -174,7 +174,6 @@ export class Realm {
this.react = {
enabled: opts.reactEnabled || false,
output: opts.reactOutput || "create-element",
flowRequired: true,
symbols: new Map(),
currentOwner: undefined,
reactLibraryObject: undefined,
@ -226,7 +225,6 @@ export class Realm {
react: {
enabled: boolean,
output?: ReactOutputTypes,
flowRequired: boolean,
symbols: Map<ReactSymbolTypes, SymbolValue>,
currentOwner?: ObjectValue,
reactLibraryObject?: ObjectValue,

View File

@ -30,7 +30,11 @@ import { ModuleTracer } from "../utils/modules.js";
import buildTemplate from "babel-template";
import { ReactStatistics, type ReactSerializerState } from "./types";
import { Reconciler } from "../react/reconcilation.js";
import { valueIsClassComponent, convertSimpleClassComponentToFunctionalComponent } from "../react/utils.js";
import {
valueIsClassComponent,
convertSimpleClassComponentToFunctionalComponent,
normalizeFunctionalComponentParamaters,
} from "../react/utils.js";
import * as t from "babel-types";
export class Functions {
@ -136,6 +140,7 @@ export class Functions {
// if the root component was a class and is now simple, we can convert it from a class
// component to a functional component
convertSimpleClassComponentToFunctionalComponent(this.realm, componentType, additionalFunctionEffects);
normalizeFunctionalComponentParamaters(componentType);
this.writeEffects.set(componentType, additionalFunctionEffects);
} else if (valueIsClassComponent(this.realm, componentType)) {
let prototype = Get(this.realm, componentType, "prototype");
@ -144,6 +149,7 @@ export class Functions {
invariant(renderMethod instanceof ECMAScriptSourceFunctionValue);
this.writeEffects.set(renderMethod, additionalFunctionEffects);
} else {
normalizeFunctionalComponentParamaters(componentType);
this.writeEffects.set(componentType, additionalFunctionEffects);
}
}

View File

@ -16,7 +16,6 @@ import { AbruptCompletion } from "../completions.js";
import { Generator } from "../utils/generator.js";
import generate from "babel-generator";
import traverseFast from "../utils/traverse-fast.js";
import { stripFlowTypeAnnotations } from "../flow/utils.js";
import invariant from "../invariant.js";
import type { SerializerOptions } from "../options.js";
import { TimingStatistics, SerializerStatistics, ReactStatistics } from "./types.js";
@ -217,9 +216,7 @@ export class Serializer {
);
let ast = residualHeapSerializer.serialize();
if (this.realm.react.enabled && this.realm.react.flowRequired) {
stripFlowTypeAnnotations(ast);
}
// the signature for generate is not complete, hence the any
let generated = generate(ast, { sourceMaps: sourceMaps }, (code: any));
if (timingStats !== undefined) {

View File

@ -30,9 +30,6 @@ export default function(
let plugins = [];
if (realm.react.enabled) {
plugins.push("jsx");
if (realm.react.flowRequired) {
plugins.push("flow");
}
}
let ast = parse(code, { filename, sourceType, startLine, plugins });
traverseFast(ast, node => {

View File

@ -21,6 +21,7 @@ import { FatalError } from "../errors.js";
import type { Realm } from "../realm.js";
import type { PropertyKeyValue } from "../types.js";
import { PreludeGenerator } from "../utils/generator.js";
import buildExpressionTemplate from "../utils/builder.js";
import {
AbstractObjectValue,
@ -672,4 +673,19 @@ export default class AbstractValue extends Value {
return realm.reportIntrospectionError(message);
}
static createAbstractObject(realm: Realm, name: string, template?: ObjectValue): AbstractObjectValue {
let value;
if (template === undefined) {
template = new ObjectValue(realm, realm.intrinsics.ObjectPrototype);
}
template.makePartial();
template.makeSimple();
value = AbstractValue.createFromTemplate(realm, buildExpressionTemplate(name), ObjectValue, [], name);
value.intrinsicName = name;
value.values = new ValuesDomain(new Set([template]));
realm.rebuildNestedProperties(value, name);
invariant(value instanceof AbstractObjectValue);
return value;
}
}

View File

@ -17,7 +17,7 @@ function Override(props) {
});
}
function App(props: {show: boolean}) {
function App(props) {
return (
<Override overrideShow={props.show}>
<MaybeShow show={true}>

View File

@ -6,7 +6,7 @@ function SubChild(props, context) {
return <span>The context title is: {context.title}</span>;
}
function Child(props: any, context: {title: string}) {
function Child(props, context) {
return <span><SubChild /></span>;
}

View File

@ -6,7 +6,7 @@ function Fn(props) {
return <div>Hello {props[props.dynamicKey]}</div>;
}
function App(props: {dynamicKey: string}) {
function App(props) {
return <Fn foo="World" dynamicKey={props.dynamicKey} />;
}

View File

@ -30,7 +30,7 @@ var Stateful = (function (superclass) {
return Stateful;
}(React.Component));
function App(props: {switch: boolean, x: any}) {
function App(props) {
if (props.switch) {
return (
<div>

View File

@ -37,7 +37,7 @@ function SettingsPane(props) {
return <div key='ha'><Stateful x={props.x} /></div>;
}
function App(props: {switch: boolean, x: any}) {
function App(props) {
if (props.switch) {
return (
<div>

View File

@ -6,7 +6,7 @@ function A(props) {
return <div>Hello {props.x}</div>;
}
function App(props: any) {
function App(props) {
return (
<div>
<A x={props.x.toString()} />

View File

@ -6,7 +6,7 @@ function A(props) {
return <div>Hello {props.x}</div>;
}
function App(props: any) {
function App(props) {
return (
<div>
<A x={props.toString()} />

View File

@ -6,7 +6,7 @@ function A(props) {
return props.children;
}
function App(props: any) {
function App(props) {
return (
<A>
<A>

View File

@ -13,7 +13,7 @@ function A(foo) {
);
}
function App({rootRef}: {rootRef: Function}) {
function App({rootRef}) {
return (
<div>
<A rootRef={rootRef} />

View File

@ -38,7 +38,7 @@ function SettingsPane(props) {
return <Stateful x={props.x}>Bye</Stateful>;
}
function App(props: {switch: boolean, x: any}) {
function App(props) {
if (props.switch) {
return (
<div>

View File

@ -6,7 +6,7 @@ function Foo() {
return <div>123</div>
}
function App(props: {yar: boolean}) {
function App(props) {
return <div>{props.yar ? <Foo arg={1} /> : <Foo arg={2} />}</div>;
}