mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Provide a basic recovery mechanism for for
and while
loops (#2118)
Summary: Release notes: standard for loops and while loops have a recovery mechanism in pure scope This PR provides a bail-out recovery mechanism in pure scope for FatalErrors thrown and caught from for/while loops. Until now, if a FatalError occurs from trying to evaluate abstract for/while loops, we'd have to recover at a higher point in the callstack, which wasn't always possible (the loop may be at the root of a function/component). The ideal long-term strategy is to properly model out the different cases for loops, but this is a complex and time-consuming process. This PR adds a recovery mechanism that serializes out the original for loop, but within a newly created wrapper function containing the loop logic and a function call to that newly created function wrapper. This allows us to still run the same code at runtime, where we were unable to evaluate and optimize it at build time. For now, this PR only adds recovery support for standard `for` and `while` loops (as they go through the same code path). We already have some basic evaluation for `do while` loops, but trying to adapt that code to work with the failing test case (https://github.com/facebook/prepack/issues/2055) didn't work for me – we have so many strange problems to deal with first before we can properly handle that issue. In cases where the loop uses `this`, the context is found and correctly called with the wrapper function. In cases of usage of `return`, `arguments` and labelled `break/continue` we bail out again as this is not currently supported in the scope of this PR (can be added in a follow up PR, but I wanted to keep the scope of this PR limited). For example, take this failing case on master: ```js function fn(props, splitPoint) { var text = props.text || ""; text = text.replace(/\s*$/, ""); if (splitPoint !== null) { while (text[splitPoint - 1] === "\n") { splitPoint--; } } return splitPoint; } ``` This serializes out to: ```js var _0 = function (props, splitPoint) { var __scope_0 = new Array(1); var __get_scope_binding_0 = function (__selector) { var __captured; switch (__selector) { case 0: __captured = [_G, _E]; break; } __scope_0[__selector] = __captured; return __captured; }; var _C = function () { var __captured__scope_1 = __scope_0[0] || __get_scope_binding_0(0); for (; __captured__scope_1[1][__captured__scope_1[0] - 1] === "\n";) { __captured__scope_1[0]--; } }; var _$0 = props.text; var _$2 = (_$0 || "").replace(/\s*$/, ""); var _6 = splitPoint !== null; var _G = _6 ? void 0 : splitPoint; var _E = _6 ? void 0 : _$2; if (_6) { (__scope_0[0] || __get_scope_binding_0(0))[0] = splitPoint; (__scope_0[0] || __get_scope_binding_0(0))[1] = _$2; var _$5 = _C(); } var _$6 = (__scope_0[0] || __get_scope_binding_0(0))[0]; return _$6; }; ``` Furthermore, an idea that might be a good follow up PR would be to break the wrapper function into two functions, depending on some heuristics. If we can detect that the loop body does not have any unsupported side-effects like writing to variables that are havoced etc, we can then tell Prepack to optimize the inner function wrapper for the loop body. That way, at least some parts of the loop get optimized (the outer bindings will be havoced before this point, so it should work okay). I think I suggested this in person with hermanventer and sebmarkbage in Seattle as a potential way to optimize complex loops that we can't compute the fixed point for right now. Fixes #2055 Closes https://github.com/facebook/prepack/pull/2118 Differential Revision: D8410341 Pulled By: trueadm fbshipit-source-id: ee7e6b1bc1feadf0c924e4f82506ca32ca1dadc9
This commit is contained in:
parent
38392a5499
commit
5a5a100483
@ -11,7 +11,7 @@
|
||||
|
||||
import type { LexicalEnvironment } from "../environment.js";
|
||||
import type { Realm } from "../realm.js";
|
||||
import { Value, EmptyValue } from "../values/index.js";
|
||||
import { AbstractValue, Value, EmptyValue, ECMAScriptSourceFunctionValue } from "../values/index.js";
|
||||
import {
|
||||
AbruptCompletion,
|
||||
BreakCompletion,
|
||||
@ -20,14 +20,29 @@ import {
|
||||
ForkedAbruptCompletion,
|
||||
PossiblyNormalCompletion,
|
||||
ReturnCompletion,
|
||||
SimpleNormalCompletion,
|
||||
ThrowCompletion,
|
||||
} from "../completions.js";
|
||||
import traverse from "babel-traverse";
|
||||
import type { BabelTraversePath } from "babel-traverse";
|
||||
import { TypesDomain, ValuesDomain } from "../domains/index.js";
|
||||
import { CompilerDiagnostic, FatalError } from "../errors.js";
|
||||
import { UpdateEmpty } from "../methods/index.js";
|
||||
import { LoopContinues, InternalGetResultValue, TryToApplyEffectsOfJoiningBranches } from "./ForOfStatement.js";
|
||||
import { Environment, Functions, Join, To } from "../singletons.js";
|
||||
import { Environment, Functions, Havoc, Join, To } from "../singletons.js";
|
||||
import invariant from "../invariant.js";
|
||||
import type { BabelNodeForStatement } from "babel-types";
|
||||
import * as t from "babel-types";
|
||||
import type { FunctionBodyAstNode } from "../types.js";
|
||||
import type { BabelNodeExpression, BabelNodeForStatement, BabelNodeBlockStatement } from "babel-types";
|
||||
|
||||
type BailOutWrapperInfo = {
|
||||
usesArguments: boolean,
|
||||
usesThis: boolean,
|
||||
usesReturn: boolean,
|
||||
usesGotoToLabel: boolean,
|
||||
usesThrow: boolean,
|
||||
varPatternUnsupported: boolean,
|
||||
};
|
||||
|
||||
// ECMA262 13.7.4.9
|
||||
export function CreatePerIterationEnvironment(realm: Realm, perIterationBindings: Array<string>) {
|
||||
@ -268,6 +283,194 @@ function ForBodyEvaluation(
|
||||
}
|
||||
}
|
||||
|
||||
let BailOutWrapperClosureRefVisitor = {
|
||||
ReferencedIdentifier(path: BabelTraversePath, state: BailOutWrapperInfo) {
|
||||
if (path.node.name === "arguments") {
|
||||
state.usesArguments = true;
|
||||
}
|
||||
},
|
||||
ThisExpression(path: BabelTraversePath, state: BailOutWrapperInfo) {
|
||||
state.usesThis = true;
|
||||
},
|
||||
"BreakStatement|ContinueStatement"(path: BabelTraversePath, state: BailOutWrapperInfo) {
|
||||
if (path.node.label !== null) {
|
||||
state.usesGotoToLabel = true;
|
||||
}
|
||||
},
|
||||
ReturnStatement(path: BabelTraversePath, state: BailOutWrapperInfo) {
|
||||
state.usesReturn = true;
|
||||
},
|
||||
ThrowStatement(path: BabelTraversePath, state: BailOutWrapperInfo) {
|
||||
state.usesThrow = true;
|
||||
},
|
||||
VariableDeclaration(path: BabelTraversePath, state: BailOutWrapperInfo) {
|
||||
let node = path.node;
|
||||
// If our parent is a for loop (there are 3 kinds) we do not need a wrapper
|
||||
// i.e. for (var x of y) for (var x in y) for (var x; x < y; x++)
|
||||
let needsExpressionWrapper =
|
||||
!t.isForStatement(path.parentPath.node) &&
|
||||
!t.isForOfStatement(path.parentPath.node) &&
|
||||
!t.isForInStatement(path.parentPath.node);
|
||||
|
||||
const getConvertedDeclarator = index => {
|
||||
let { id, init } = node.declarations[index];
|
||||
|
||||
if (t.isIdentifier(id)) {
|
||||
return t.assignmentExpression("=", id, init);
|
||||
} else {
|
||||
// We do not currently support ObjectPattern, SpreadPattern and ArrayPattern
|
||||
// see: https://github.com/babel/babylon/blob/master/ast/spec.md#patterns
|
||||
state.varPatternUnsupported = true;
|
||||
}
|
||||
};
|
||||
|
||||
if (node.kind === "var") {
|
||||
if (node.declarations.length === 1) {
|
||||
let convertedNodeOrUndefined = getConvertedDeclarator(0);
|
||||
if (convertedNodeOrUndefined === undefined) {
|
||||
// Do not continue as we don't support this
|
||||
return;
|
||||
}
|
||||
path.replaceWith(
|
||||
needsExpressionWrapper ? t.expressionStatement(convertedNodeOrUndefined) : convertedNodeOrUndefined
|
||||
);
|
||||
} else {
|
||||
// convert to sequence, so: `var x = 1, y = 2;` becomes `x = 1, y = 2;`
|
||||
let expressions = [];
|
||||
for (let i = 0; i < node.declarations.length; i++) {
|
||||
let convertedNodeOrUndefined = getConvertedDeclarator(i);
|
||||
if (convertedNodeOrUndefined === undefined) {
|
||||
// Do not continue as we don't support this
|
||||
return;
|
||||
}
|
||||
expressions.push(convertedNodeOrUndefined);
|
||||
}
|
||||
let sequenceExpression = t.sequenceExpression(((expressions: any): Array<BabelNodeExpression>));
|
||||
path.replaceWith(needsExpressionWrapper ? t.expressionStatement(sequenceExpression) : sequenceExpression);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function generateRuntimeForStatement(
|
||||
ast: BabelNodeForStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): AbstractValue {
|
||||
let wrapperFunction = new ECMAScriptSourceFunctionValue(realm);
|
||||
let body = ((t.cloneDeep(t.blockStatement([ast])): any): BabelNodeBlockStatement);
|
||||
((body: any): FunctionBodyAstNode).uniqueOrderedTag = realm.functionBodyUniqueTagSeed++;
|
||||
wrapperFunction.$ECMAScriptCode = body;
|
||||
wrapperFunction.$FormalParameters = [];
|
||||
wrapperFunction.$Environment = env;
|
||||
// We need to scan to AST looking for "this", "return", "throw", labels and "arguments"
|
||||
let functionInfo = {
|
||||
usesArguments: false,
|
||||
usesThis: false,
|
||||
usesReturn: false,
|
||||
usesGotoToLabel: false,
|
||||
usesThrow: false,
|
||||
varPatternUnsupported: false,
|
||||
};
|
||||
|
||||
traverse(
|
||||
t.file(t.program([t.expressionStatement(t.functionExpression(null, [], body))])),
|
||||
BailOutWrapperClosureRefVisitor,
|
||||
null,
|
||||
functionInfo
|
||||
);
|
||||
traverse.clearCache();
|
||||
let { usesReturn, usesThrow, usesArguments, usesGotoToLabel, varPatternUnsupported, usesThis } = functionInfo;
|
||||
|
||||
if (usesReturn || usesThrow || usesArguments || usesGotoToLabel || varPatternUnsupported) {
|
||||
// We do not have support for these yet
|
||||
let diagnostic = new CompilerDiagnostic(
|
||||
`failed to recover from a for/while loop bail-out due to unsupported logic in loop body`,
|
||||
realm.currentLocation,
|
||||
"PP0037",
|
||||
"FatalError"
|
||||
);
|
||||
realm.handleError(diagnostic);
|
||||
throw new FatalError();
|
||||
}
|
||||
let args = [wrapperFunction];
|
||||
|
||||
if (usesThis) {
|
||||
let thisRef = env.evaluate(t.thisExpression(), strictCode);
|
||||
let thisVal = Environment.GetValue(realm, thisRef);
|
||||
Havoc.value(realm, thisVal);
|
||||
args.push(thisVal);
|
||||
}
|
||||
|
||||
// We havoc the wrapping function value, which in turn invokes the havocing
|
||||
// logic which is transitive. The havocing logic should recursively visit
|
||||
// all bindings/objects in the loop and its body and mark the associated
|
||||
// bindings/objects that do havoc appropiately.
|
||||
Havoc.value(realm, wrapperFunction);
|
||||
|
||||
let wrapperValue = AbstractValue.createTemporalFromBuildFunction(
|
||||
realm,
|
||||
Value,
|
||||
args,
|
||||
([func, thisExpr]) =>
|
||||
usesThis
|
||||
? t.callExpression(t.memberExpression(func, t.identifier("call")), [thisExpr])
|
||||
: t.callExpression(func, [])
|
||||
);
|
||||
invariant(wrapperValue instanceof AbstractValue);
|
||||
return wrapperValue;
|
||||
}
|
||||
|
||||
function tryToEvaluateForStatementOrLeaveAsAbstract(
|
||||
ast: BabelNodeForStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): Value {
|
||||
let effects;
|
||||
let savedSuppressDiagnostics = realm.suppressDiagnostics;
|
||||
try {
|
||||
realm.suppressDiagnostics = true;
|
||||
effects = realm.evaluateForEffects(
|
||||
() => evaluateForStatement(ast, strictCode, env, realm, labelSet),
|
||||
undefined,
|
||||
"tryToEvaluateForStatementOrLeaveAsAbstract"
|
||||
);
|
||||
} catch (error) {
|
||||
if (error instanceof FatalError) {
|
||||
realm.suppressDiagnostics = savedSuppressDiagnostics;
|
||||
return realm.evaluateWithPossibleThrowCompletion(
|
||||
() => generateRuntimeForStatement(ast, strictCode, env, realm, labelSet),
|
||||
TypesDomain.topVal,
|
||||
ValuesDomain.topVal
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
realm.suppressDiagnostics = savedSuppressDiagnostics;
|
||||
}
|
||||
// Note that the effects of (non joining) abrupt branches are not included
|
||||
// in effects, but are tracked separately inside completion.
|
||||
realm.applyEffects(effects);
|
||||
let completion = effects.result;
|
||||
if (completion instanceof PossiblyNormalCompletion) {
|
||||
// in this case one of the branches may complete abruptly, which means that
|
||||
// not all control flow branches join into one flow at this point.
|
||||
// Consequently we have to continue tracking changes until the point where
|
||||
// all the branches come together into one.
|
||||
completion = realm.composeWithSavedCompletion(completion);
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
}
|
||||
|
||||
// ECMA262 13.7.4.7
|
||||
export default function(
|
||||
ast: BabelNodeForStatement,
|
||||
@ -275,6 +478,20 @@ export default function(
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): Value {
|
||||
if (realm.isInPureScope()) {
|
||||
return tryToEvaluateForStatementOrLeaveAsAbstract(ast, strictCode, env, realm, labelSet);
|
||||
} else {
|
||||
return evaluateForStatement(ast, strictCode, env, realm, labelSet);
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateForStatement(
|
||||
ast: BabelNodeForStatement,
|
||||
strictCode: boolean,
|
||||
env: LexicalEnvironment,
|
||||
realm: Realm,
|
||||
labelSet: ?Array<string>
|
||||
): Value {
|
||||
let { init, test, update, body } = ast;
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
import { Realm, Effects } from "../realm.js";
|
||||
import { ValuesDomain } from "../domains/index.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion } from "../completions.js";
|
||||
import { AbruptCompletion, PossiblyNormalCompletion, SimpleNormalCompletion } from "../completions.js";
|
||||
import type { BabelNode, BabelNodeJSXIdentifier, BabelNodeExpression } from "babel-types";
|
||||
import { parseExpression } from "babylon";
|
||||
import {
|
||||
@ -843,6 +843,7 @@ export function getValueFromFunctionCall(
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
invariant(completion instanceof Value);
|
||||
return completion;
|
||||
}
|
||||
@ -1101,6 +1102,7 @@ export function applyObjectAssignConfigsForReactElement(realm: Realm, to: Object
|
||||
}
|
||||
// return or throw completion
|
||||
if (completion instanceof AbruptCompletion) throw completion;
|
||||
if (completion instanceof SimpleNormalCompletion) completion = completion.value;
|
||||
};
|
||||
|
||||
if (realm.isInPureScope()) {
|
||||
|
18
test/serializer/optimized-functions/LoopBailout.js
Normal file
18
test/serializer/optimized-functions/LoopBailout.js
Normal file
@ -0,0 +1,18 @@
|
||||
function fn(props, splitPoint) {
|
||||
var text = props.text || "";
|
||||
|
||||
text = text.replace(/\s*$/, "");
|
||||
|
||||
if (splitPoint !== null) {
|
||||
while (text[splitPoint - 1] === "\n") {
|
||||
splitPoint--;
|
||||
}
|
||||
}
|
||||
return splitPoint;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn({text: "foo\nfoo"}, 5);
|
||||
};
|
15
test/serializer/optimized-functions/LoopBailout10.js
Normal file
15
test/serializer/optimized-functions/LoopBailout10.js
Normal file
@ -0,0 +1,15 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
for (; i !== x;) {
|
||||
counter.x++;
|
||||
i++;
|
||||
var val1 = counter.x, val2 = val1 + 1;
|
||||
}
|
||||
return val2;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2});
|
||||
};
|
16
test/serializer/optimized-functions/LoopBailout11.js
Normal file
16
test/serializer/optimized-functions/LoopBailout11.js
Normal file
@ -0,0 +1,16 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
var val2 = undefined;
|
||||
for (; i !== x;) {
|
||||
counter.x++;
|
||||
i++;
|
||||
val2 = i;
|
||||
}
|
||||
return val2;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2});
|
||||
};
|
17
test/serializer/optimized-functions/LoopBailout12.js
Normal file
17
test/serializer/optimized-functions/LoopBailout12.js
Normal file
@ -0,0 +1,17 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
var val2 = undefined;
|
||||
for (; i !== x;) {
|
||||
var foo = {};
|
||||
counter.x++;
|
||||
i++;
|
||||
foo.x = x;
|
||||
}
|
||||
return foo;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2}).x;
|
||||
};
|
17
test/serializer/optimized-functions/LoopBailout13.js
Normal file
17
test/serializer/optimized-functions/LoopBailout13.js
Normal file
@ -0,0 +1,17 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
var val2 = undefined;
|
||||
for (; i !== x;) {
|
||||
var foo = {};
|
||||
counter.x++;
|
||||
i++;
|
||||
foo.counter = counter;
|
||||
}
|
||||
return foo;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2}).counter.x;
|
||||
};
|
17
test/serializer/optimized-functions/LoopBailout14.js
Normal file
17
test/serializer/optimized-functions/LoopBailout14.js
Normal file
@ -0,0 +1,17 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
var val2 = undefined;
|
||||
for (; i !== x;) {
|
||||
var foo = {};
|
||||
counter.x++;
|
||||
i++;
|
||||
foo.counter = counter;
|
||||
}
|
||||
return foo.counter.x + 10;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2});
|
||||
};
|
18
test/serializer/optimized-functions/LoopBailout15.js
Normal file
18
test/serializer/optimized-functions/LoopBailout15.js
Normal file
@ -0,0 +1,18 @@
|
||||
function fn(secondaryPattern, replaceWith, wholeNumber) {
|
||||
var replaced;
|
||||
while (
|
||||
(replaced = wholeNumber.replace(
|
||||
secondaryPattern,
|
||||
replaceWith
|
||||
)) != wholeNumber
|
||||
) {
|
||||
wholeNumber = replaced;
|
||||
}
|
||||
return wholeNumber;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn("1", "2", "121341");
|
||||
};
|
16
test/serializer/optimized-functions/LoopBailout16.js
Normal file
16
test/serializer/optimized-functions/LoopBailout16.js
Normal file
@ -0,0 +1,16 @@
|
||||
function fn(x, total, val) {
|
||||
var replaced;
|
||||
let wholeNumber = val.toString();
|
||||
if (x === true) {
|
||||
for (var i = 0; i < total; i++) {
|
||||
wholeNumber++;
|
||||
}
|
||||
}
|
||||
return wholeNumber;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(false, 10, 5);
|
||||
};
|
111
test/serializer/optimized-functions/LoopBailout17.js
Normal file
111
test/serializer/optimized-functions/LoopBailout17.js
Normal file
@ -0,0 +1,111 @@
|
||||
(function() {
|
||||
|
||||
global._DateFormatConfig = {
|
||||
formats: {
|
||||
'l, F j, Y': 'l, F j, Y',
|
||||
},
|
||||
}
|
||||
|
||||
var DateFormatConfig = global.__abstract ? __abstract({}, "(global._DateFormatConfig)") : global._DateFormatConfig;
|
||||
global.__makeSimple && __makeSimple(DateFormatConfig);
|
||||
|
||||
var MONTH_NAMES = void 0;
|
||||
var WEEKDAY_NAMES = void 0;
|
||||
|
||||
var DateStrings = {
|
||||
getWeekdayName: function getWeekdayName(weekday) {
|
||||
if (!WEEKDAY_NAMES) {
|
||||
WEEKDAY_NAMES = [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday"
|
||||
];
|
||||
}
|
||||
|
||||
return WEEKDAY_NAMES[weekday];
|
||||
},
|
||||
_initializeMonthNames: function _initializeMonthNames() {
|
||||
MONTH_NAMES = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"
|
||||
];
|
||||
},
|
||||
|
||||
getMonthName: function getMonthName(month) {
|
||||
if (!MONTH_NAMES) {
|
||||
DateStrings._initializeMonthNames();
|
||||
}
|
||||
|
||||
return MONTH_NAMES[month - 1];
|
||||
},
|
||||
};
|
||||
|
||||
function formatDate(date, format, options) {
|
||||
options = options || {};
|
||||
|
||||
if (typeof date === "string") {
|
||||
date = parseInt(date, 10);
|
||||
}
|
||||
if (typeof date === "number") {
|
||||
date = new Date(date * 1000);
|
||||
}
|
||||
var localizedFormat = DateFormatConfig.formats[format];
|
||||
var prefix = "getUTC";
|
||||
var dateDay = date[prefix + "Date"]();
|
||||
var dateDayOfWeek = date[prefix + "Day"]();
|
||||
var dateMonth = date[prefix + "Month"]();
|
||||
var dateYear = date[prefix + "FullYear"]();
|
||||
|
||||
var output = "";
|
||||
for (var i = 0; i < localizedFormat.length; i++) {
|
||||
var character = localizedFormat.charAt(i);
|
||||
|
||||
switch (character) {
|
||||
case "j":
|
||||
output += dateDay;
|
||||
break;
|
||||
case "l":
|
||||
output += DateStrings.getWeekdayName(dateDayOfWeek);
|
||||
break;
|
||||
case "F":
|
||||
case "f":
|
||||
output += DateStrings.getMonthName(dateMonth + 1);
|
||||
break;
|
||||
case "Y":
|
||||
output += dateYear;
|
||||
break;
|
||||
default:
|
||||
output += character;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function fn(a, b) {
|
||||
return formatDate(a, "l, F j, Y");
|
||||
}
|
||||
|
||||
global.fn = fn;
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
global.inspect = function() {
|
||||
return JSON.stringify(global.fn("1529579851072"));
|
||||
}
|
||||
|
||||
})();
|
14
test/serializer/optimized-functions/LoopBailout2.js
Normal file
14
test/serializer/optimized-functions/LoopBailout2.js
Normal file
@ -0,0 +1,14 @@
|
||||
function fn(x, oldItems) {
|
||||
var items = [];
|
||||
for (let i = 0; i < x; i++) {
|
||||
var oldItem = oldItems[i];
|
||||
items.push(oldItem + 2);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return JSON.stringify(fn(5, [1,2,3,4,5]));
|
||||
};
|
16
test/serializer/optimized-functions/LoopBailout3.js
Normal file
16
test/serializer/optimized-functions/LoopBailout3.js
Normal file
@ -0,0 +1,16 @@
|
||||
function Model(x, oldItems) {
|
||||
this.oldItems = oldItems;
|
||||
var items = [];
|
||||
for (let i = 0; i < x; i++) {
|
||||
var oldItem = this.oldItems[i];
|
||||
items.push(oldItem + 2);
|
||||
}
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(Model);
|
||||
|
||||
inspect = function() {
|
||||
var model = new Model(5, [1,2,3,4,5]);
|
||||
return JSON.stringify(model.items);
|
||||
};
|
15
test/serializer/optimized-functions/LoopBailout4.js
Normal file
15
test/serializer/optimized-functions/LoopBailout4.js
Normal file
@ -0,0 +1,15 @@
|
||||
function Model(x, oldItems) {
|
||||
|
||||
this.items = [];
|
||||
for (let i = 0; i < x; i++) {
|
||||
var oldItem = oldItems[i];
|
||||
this.items.push(oldItem + 2);
|
||||
}
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(Model);
|
||||
|
||||
inspect = function() {
|
||||
var model = new Model(5, [1,2,3,4,5]);
|
||||
return JSON.stringify(model.items);
|
||||
};
|
16
test/serializer/optimized-functions/LoopBailout5.js
Normal file
16
test/serializer/optimized-functions/LoopBailout5.js
Normal file
@ -0,0 +1,16 @@
|
||||
function Model(x, oldItems) {
|
||||
this.items = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
for (let i = 0; i < x; i++) {
|
||||
var oldItem = oldItems[i];
|
||||
this.items.push(oldItem + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(Model);
|
||||
|
||||
inspect = function() {
|
||||
var model = new Model(5, [1,2,3,4,5]);
|
||||
return JSON.stringify(model.items);
|
||||
};
|
16
test/serializer/optimized-functions/LoopBailout6.js
Normal file
16
test/serializer/optimized-functions/LoopBailout6.js
Normal file
@ -0,0 +1,16 @@
|
||||
function fn(x, oldItems) {
|
||||
var items = [];
|
||||
var i = 0;
|
||||
while (i < x) {
|
||||
var oldItem = oldItems[i];
|
||||
items.push(oldItem + 2);
|
||||
i++;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return JSON.stringify(fn(5, [1,2,3,4,5]));
|
||||
};
|
15
test/serializer/optimized-functions/LoopBailout7.js
Normal file
15
test/serializer/optimized-functions/LoopBailout7.js
Normal file
@ -0,0 +1,15 @@
|
||||
function fn(x, oldItems) {
|
||||
var items = [];
|
||||
for (; i !== x;) {
|
||||
var oldItem = oldItems[i];
|
||||
items.push(oldItem + 2);
|
||||
i++;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return JSON.stringify(fn(5, [1,2,3,4,5]));
|
||||
};
|
14
test/serializer/optimized-functions/LoopBailout8.js
Normal file
14
test/serializer/optimized-functions/LoopBailout8.js
Normal file
@ -0,0 +1,14 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
for (; i !== x;) {
|
||||
counter.x++;
|
||||
i++;
|
||||
}
|
||||
return counter.x;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2});
|
||||
};
|
15
test/serializer/optimized-functions/LoopBailout9.js
Normal file
15
test/serializer/optimized-functions/LoopBailout9.js
Normal file
@ -0,0 +1,15 @@
|
||||
function fn(x, counter) {
|
||||
var i = 0;
|
||||
for (; i !== x;) {
|
||||
counter.x++;
|
||||
i++;
|
||||
var val = counter.x
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
global.__optimize && __optimize(fn);
|
||||
|
||||
inspect = function() {
|
||||
return fn(100, {x: 2});
|
||||
};
|
Loading…
Reference in New Issue
Block a user