Adds React pe-functional-components benchmark and some React SSR changes (#2560)

Summary:
Release notes: none

All these changes are only internal changes related to React.

This PR adds the `pe-functional-components` benchmark as tests to the React reconciler. The test was taken from: https://github.com/facebook/react/tree/master/scripts/bench/benchmarks/pe-functional-components.

In order to make the server side renderer test pass, a few TODOs had to be filled in (logic was missing) and the JSON logic has to be updated to account for empty strings in children that the compiler merges.
Pull Request resolved: https://github.com/facebook/prepack/pull/2560

Differential Revision: D10008375

Pulled By: trueadm

fbshipit-source-id: 3b39a3e6387e23e17532a2343bd84ebebb7ee9cd
This commit is contained in:
Dominic Gannaway 2018-09-24 03:52:26 -07:00 committed by Facebook Github Bot
parent 465b8defba
commit 0e52d02190
9 changed files with 23177 additions and 10 deletions

View File

@ -14,7 +14,15 @@
// security holes in the string escaping because of this.
import type { Realm } from "../../realm.js";
import { AbstractValue, BooleanValue, FunctionValue, NumberValue, SymbolValue, Value } from "../../values/index.js";
import {
AbstractValue,
BooleanValue,
FunctionValue,
NumberValue,
StringValue,
SymbolValue,
Value,
} from "../../values/index.js";
import invariant from "../../invariant.js";
type PropertyType = 0 | 1 | 2 | 3 | 4 | 5 | 6;
@ -389,12 +397,12 @@ export function shouldRemoveAttribute(
}
return invariant(false, "TODO");
case NUMERIC:
if (value instanceof NumberValue) {
if (value instanceof NumberValue || value instanceof StringValue) {
return isNaN(value.value);
}
return invariant(false, "TODO");
case POSITIVE_NUMERIC:
if (value instanceof NumberValue) {
if (value instanceof NumberValue || value instanceof StringValue) {
return isNaN(value.value) || (value.value: any) < 1;
}
return invariant(false, "TODO");

View File

@ -32,6 +32,7 @@ import {
} from "../../values/index.js";
import { Reconciler } from "../reconcilation.js";
import {
applyObjectAssignConfigsForReactElement,
createReactEvaluatedNode,
forEachArrayValue,
getComponentName,
@ -67,7 +68,7 @@ import {
} from "./dom-config.js";
// $FlowFixMe: flow complains that this isn't a module but it is, and it seems to load fine
import hyphenateStyleName from "fbjs/lib/hyphenateStyleName";
import { To } from "../../singletons.js";
import { Properties, To } from "../../singletons.js";
import { createOperationDescriptor } from "../../utils/generator.js";
export type ReactNode = Array<ReactNode> | string | AbstractValue | ArrayValue;
@ -139,12 +140,14 @@ function createMarkupForProperty(
const { type } = propertyInfo;
if (type === BOOLEAN || (type === OVERLOADED_BOOLEAN && value === true)) {
return attributeName + '=""';
} else if (value instanceof StringValue || value instanceof NumberValue) {
} else if (value instanceof StringValue || value instanceof NumberValue || value instanceof BooleanValue) {
// $FlowFixMe: Flow complains about booleans being converted to strings, which is the intention
return attributeName + "=" + quoteAttributeValueForBrowser(value.value + "");
} else if (value instanceof AbstractValue) {
return ([attributeName + "=", renderValueWithHelper(realm, value, htmlEscapeHelper)]: Array<ReactNode>);
}
} else if (value instanceof StringValue || value instanceof NumberValue) {
} else if (value instanceof StringValue || value instanceof NumberValue || value instanceof BooleanValue) {
// $FlowFixMe: Flow complains about booleans being converted to strings, which is the intention
return name + "=" + quoteAttributeValueForBrowser(value.value + "");
} else if (value instanceof AbstractValue) {
return ([name + '="', renderValueWithHelper(realm, value, htmlEscapeHelper), '"']: Array<ReactNode>);
@ -393,9 +396,46 @@ class ReactDOMServerRenderer {
let tag = type.toLowerCase();
if (tag === "input") {
invariant(false, "TODO");
let defaultValueProp = getProperty(this.realm, propsValue, "defaultValue");
let defaultCheckedProp = getProperty(this.realm, propsValue, "defaultChecked");
let valueProp = getProperty(this.realm, propsValue, "value");
let checkedProp = getProperty(this.realm, propsValue, "checked");
let newProps = new ObjectValue(this.realm, this.realm.intrinsics.ObjectPrototype);
Properties.Set(this.realm, newProps, "type", this.realm.intrinsics.undefined, true);
let inputProps = new ObjectValue(this.realm, this.realm.intrinsics.ObjectPrototype);
Properties.Set(this.realm, inputProps, "defaultChecked", this.realm.intrinsics.undefined, true);
Properties.Set(this.realm, inputProps, "defaultValue", this.realm.intrinsics.undefined, true);
Properties.Set(
this.realm,
inputProps,
"value",
valueProp !== this.realm.intrinsics.null ? valueProp : defaultValueProp,
true
);
Properties.Set(
this.realm,
inputProps,
"checked",
checkedProp !== this.realm.intrinsics.null ? checkedProp : defaultCheckedProp,
true
);
applyObjectAssignConfigsForReactElement(this.realm, newProps, [propsValue, inputProps]);
propsValue = newProps;
} else if (tag === "textarea") {
invariant(false, "TODO");
let initialValue = getProperty(this.realm, propsValue, "value");
if (initialValue === this.realm.intrinsics.null) {
invariant(false, "TODO");
}
let newProps = new ObjectValue(this.realm, this.realm.intrinsics.ObjectPrototype);
let textareaProps = new ObjectValue(this.realm, this.realm.intrinsics.ObjectPrototype);
Properties.Set(this.realm, textareaProps, "value", this.realm.intrinsics.undefined, true);
Properties.Set(this.realm, textareaProps, "children", initialValue, true);
applyObjectAssignConfigsForReactElement(this.realm, newProps, [propsValue, textareaProps]);
propsValue = newProps;
} else if (tag === "select") {
invariant(false, "TODO");
} else if (tag === "option") {

View File

@ -37,14 +37,14 @@ export function mergeAdjacentJSONTextNodes(node: JSON, removeFunctions: boolean,
concatString = child;
}
} else if (typeof child === "object" && child !== null) {
if (concatString !== null) {
if (concatString !== null && concatString !== "") {
arr.push(concatString);
concatString = null;
}
arr.push(((mergeAdjacentJSONTextNodes(child, removeFunctions, visitedNodes): any): JSON));
}
}
if (concatString !== null) {
if (concatString !== null && concatString !== "") {
arr.push(concatString);
}
return arr;

View File

@ -147,3 +147,7 @@ it("Hacker News app", () => {
it("Function bind", () => {
runTest(__dirname + "/FBMocks/function-bind.js");
});
it("PE Functional Components benchmark", () => {
runTest(__dirname + "/FBMocks/pe-functional-benchmark.js");
});

File diff suppressed because it is too large Load Diff

View File

@ -20,3 +20,7 @@ it("Hacker News app", () => {
let data = JSON.parse(fs.readFileSync(__dirname + "/ServerRendering/hacker-news.json").toString());
runTest(__dirname + "/ServerRendering/hacker-news.js", { data });
});
it("PE Functional Components benchmark", () => {
runTest(__dirname + "/ServerRendering/pe-functional-benchmark.js");
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -39,3 +39,43 @@ ReactStatistics {
"optimizedTrees": 0,
}
`;
exports[`PE Functional Components benchmark: (JSX => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 0,
"evaluatedRootNodes": Array [],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 0,
}
`;
exports[`PE Functional Components benchmark: (JSX => createElement) 1`] = `
ReactStatistics {
"componentsEvaluated": 0,
"evaluatedRootNodes": Array [],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 0,
}
`;
exports[`PE Functional Components benchmark: (createElement => JSX) 1`] = `
ReactStatistics {
"componentsEvaluated": 0,
"evaluatedRootNodes": Array [],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 0,
}
`;
exports[`PE Functional Components benchmark: (createElement => createElement) 1`] = `
ReactStatistics {
"componentsEvaluated": 0,
"evaluatedRootNodes": Array [],
"inlinedComponents": 0,
"optimizedNestedClosures": 0,
"optimizedTrees": 0,
}
`;