Source/JavaScriptCore/ChangeLog

 12015-02-19 Michael Saboff <msaboff@apple.com>
 2
 3 DFG JIT needs to check for stack overflow at the start of Program and Eval execution
 4 https://bugs.webkit.org/show_bug.cgi?id=141676
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Added stack check to the beginning of the code the DFG copmiler emits for Program and Eval nodes.
 9 To aid in testing the code, I replaced the EvalCodeCache::maxCacheableSourceLength const
 10 a options in runtime/Options.h. The test script, run-jsc-stress-tests, sets that option
 11 to a huge value when running with the "Eager" options. This allows the updated test to
 12 reliably exercise the code in questions.
 13
 14 * dfg/DFGJITCompiler.cpp:
 15 (JSC::DFG::JITCompiler::compile):
 16 Added stack check.
 17
 18 * bytecode/EvalCodeCache.h:
 19 (JSC::EvalCodeCache::tryGet):
 20 (JSC::EvalCodeCache::getSlow):
 21 * runtime/Options.h:
 22 Replaced EvalCodeCache::imaxCacheableSourceLength with Options::maximumEvalCacheableSourceLength
 23 so that it can be configured when running the related test.
 24
1252015-02-19 Dean Jackson <dino@apple.com>
226
327 ES6: Implement Array.from()
180384

Source/JavaScriptCore/bytecode/EvalCodeCache.h

3131
3232#include "Executable.h"
3333#include "JSGlobalObject.h"
 34#include "Options.h"
3435#include "SourceCode.h"
3536#include <wtf/HashMap.h>
3637#include <wtf/RefPtr.h>

@@namespace JSC {
4445 public:
4546 EvalExecutable* tryGet(bool inStrictContext, const String& evalSource, JSScope* scope)
4647 {
47  if (!inStrictContext && evalSource.length() < maxCacheableSourceLength && scope->begin()->isVariableObject())
 48 if (!inStrictContext && evalSource.length() < Options::maximumEvalCacheableSourceLength() && scope->begin()->isVariableObject())
4849 return m_cacheMap.get(evalSource.impl()).get();
4950 return 0;
5051 }

@@namespace JSC {
5556 if (!evalExecutable)
5657 return 0;
5758
58  if (!inStrictContext && evalSource.length() < maxCacheableSourceLength && scope->begin()->isVariableObject() && m_cacheMap.size() < maxCacheEntries)
 59 if (!inStrictContext && evalSource.length() < Options::maximumEvalCacheableSourceLength() && scope->begin()->isVariableObject() && m_cacheMap.size() < maxCacheEntries)
5960 m_cacheMap.set(evalSource.impl(), WriteBarrier<EvalExecutable>(exec->vm(), owner, evalExecutable));
6061
6162 return evalExecutable;

@@namespace JSC {
7172 }
7273
7374 private:
74  static const unsigned maxCacheableSourceLength = 256;
7575 static const int maxCacheEntries = 64;
7676
7777 typedef HashMap<RefPtr<StringImpl>, WriteBarrier<EvalExecutable>> EvalCacheMap;
180350

Source/JavaScriptCore/dfg/DFGJITCompiler.cpp

@@void JITCompiler::compile()
293293 setStartOfCode();
294294 compileEntry();
295295 m_speculative = std::make_unique<SpeculativeJIT>(*this);
 296
 297 // Plant a check that sufficient space is available in the JSStack.
 298 addPtr(TrustedImm32(virtualRegisterForLocal(m_graph.requiredRegisterCountForExecutionAndExit() - 1).offset() * sizeof(Register)), GPRInfo::callFrameRegister, GPRInfo::regT1);
 299 Jump stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1);
 300
296301 addPtr(TrustedImm32(m_graph.stackPointerOffset() * sizeof(Register)), GPRInfo::callFrameRegister, stackPointerRegister);
297302 checkStackPointerAlignment();
298303 compileBody();
299304 setEndOfMainPath();
300305
 306 // === Footer code generation ===
 307 //
 308 // Generate the stack overflow handling; if the stack check in the entry head fails,
 309 // we need to call out to a helper function to throw the StackOverflowError.
 310 stackOverflow.link(this);
 311
 312 emitStoreCodeOrigin(CodeOrigin(0));
 313
 314 if (maxFrameExtentForSlowPathCall)
 315 addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister);
 316
 317 m_speculative->callOperationWithCallFrameRollbackOnException(operationThrowStackOverflowError, m_codeBlock);
 318
301319 // Generate slow path code.
302320 m_speculative->runSlowPathGenerators();
303321
180350

Source/JavaScriptCore/runtime/Options.h

@@typedef const char* optionString;
236236 v(unsigned, ftlOSREntryRetryThreshold, 100) \
237237 \
238238 v(int32, evalThresholdMultiplier, 10) \
 239 v(unsigned, maximumEvalCacheableSourceLength, 256) \
239240 \
240241 v(bool, randomizeExecutionCountsBetweenCheckpoints, false) \
241242 v(int32, maximumExecutionCountsBetweenCheckpointsForBaseline, 1000) \
180350

Tools/ChangeLog

 12015-02-19 Michael Saboff <msaboff@apple.com>
 2
 3 DFG JIT needs to check for stack overflow at the start of Program and Eval execution
 4 https://bugs.webkit.org/show_bug.cgi?id=141676
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Set the newly added --maximumEvalCacheableSourceLength option for eager test runs. This is needed
 9 to allow the eval out of stack tests to tier up. Without this option, we don't cache the likely
 10 large string expression that we want to eval.
 11
 12 * Scripts/run-jsc-stress-tests:
 13
1142015-02-19 Daniel Bates <dabates@apple.com>
215
316 REGRESSION (r180239): run-webkit-test fails to boot simulator device that was booted by previous test run
180384

Tools/Scripts/run-jsc-stress-tests

@@end
297297$numFailures = 0
298298
299299BASE_OPTIONS = ["--useFTLJIT=false", "--enableFunctionDotArguments=true"]
300 EAGER_OPTIONS = ["--thresholdForJITAfterWarmUp=10", "--thresholdForJITSoon=10", "--thresholdForOptimizeAfterWarmUp=20", "--thresholdForOptimizeAfterLongWarmUp=20", "--thresholdForOptimizeSoon=20", "--thresholdForFTLOptimizeAfterWarmUp=20", "--thresholdForFTLOptimizeSoon=20"]
 300EAGER_OPTIONS = ["--thresholdForJITAfterWarmUp=10", "--thresholdForJITSoon=10", "--thresholdForOptimizeAfterWarmUp=20", "--thresholdForOptimizeAfterLongWarmUp=20", "--thresholdForOptimizeSoon=20", "--thresholdForFTLOptimizeAfterWarmUp=20", "--thresholdForFTLOptimizeSoon=20", "--maximumEvalCacheableSourceLength=150000"]
301301NO_CJIT_OPTIONS = ["--enableConcurrentJIT=false", "--thresholdForJITAfterWarmUp=100"]
302302FTL_OPTIONS = ["--useFTLJIT=true"]
303303
180350

LayoutTests/ChangeLog

 12015-02-19 Michael Saboff <msaboff@apple.com>
 2
 3 DFG JIT needs to check for stack overflow at the start of Program and Eval execution
 4 https://bugs.webkit.org/show_bug.cgi?id=141676
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Updated the check for out of stack at eval entry test from using a fixed number of frame to
 9 back track to now adjust the amount of back tracking up the stack based on where we can run a
 10 simple eval(). At that point in the stack we try to cause an out of stack exception.
 11
 12 Also added a second pass of the test that takes the originally failing eval and tiers that
 13 eval expression up to the DFG when used with the agreessive options of run-jsc-stress-tests.
 14 This was done to reduce the amount of time the test takes to run in debug builds.
 15
 16 * js/regress-141098-expected.txt:
 17 * js/script-tests/regress-141098.js:
 18 (testEval):
 19 (probeAndRecurse):
 20
1212015-02-19 Myles C. Maxfield <mmaxfield@apple.com>
222
323 Updating more tests after r177774
180384

LayoutTests/js/regress-141098-expected.txt

@@Regression test for https://webkit.org/b
33On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
44
55
 6PASS Exception: RangeError: Maximum call stack size exceeded.
 7PASS Exception: RangeError: Maximum call stack size exceeded.
68PASS successfullyParsed is true
79
810TEST COMPLETE
180350

LayoutTests/js/script-tests/regress-141098.js

11description("Regression test for https://webkit.org/b/141098. Make sure eval() properly handles running out of stack space. This test should run without crashing.");
22
3 function probeAndRecurse(depth)
 3var lastEvalString = "";
 4
 5function testEval(maxIterations)
46{
57 var result;
 8 var count = 1;
69
7  // Probe stack depth
8  try {
9  result = probeAndRecurse(depth+1);
10  if (result < 0)
11  return result + 1;
12  else if (result > 0)
13  return result;
14  } catch (e) {
15  // Go up a many frames and then create an expression to eval that will consume the stack using
16  // callee registers.
17  return -60;
 10 if (!maxIterations) {
 11 var result = eval(lastEvalString);
 12 } else {
 13 for (var iter = 0; iter < maxIterations; count *= 4, iter++) {
 14 var evalString = "\"dummy\".valueOf(";
 15
 16 for (var i = 0; i < count; i++) {
 17 if (i > 0)
 18 evalString += ", ";
 19 evalString += i;
 20 }
 21
 22 evalString += ");";
 23
 24 lastEvalString = evalString;
 25 result = eval(evalString);
 26 }
1827 }
1928
20  try {
21  var count = 1;
 29 return result;
 30}
2231
23  for (var i = 0; i < 40; count *= 10, i++) {
24  evalStringPrefix = "{ var first = " + count + "; ";
25  var evalStringBody = "";
26 
27  for (var varIndex = 0; varIndex < count; varIndex++)
28  evalStringBody += "var s" + varIndex + " = " + varIndex + ";";
29 
30  evalStringBody += "var value = [";
31  for (var varIndex = 0; varIndex < count; varIndex++) {
32  if (varIndex > 0)
33  evalStringBody += ", ";
34  evalStringBody += "s" + varIndex;
35  }
36  evalStringBody += "]; ";
 32function probeAndRecurse(depth)
 33{
 34 var result;
3735
38  var evalResult = eval("{" + evalStringBody + "}");
 36 // Probe stack depth
 37 if (depth > 0) {
 38 try {
 39 result = probeAndRecurse(depth+1);
 40
 41 if (!result) {
 42 try {
 43 testEval(1);
 44 } catch (e) {
 45 return -49;
 46 }
 47 } else
 48 return result + 1
 49 } catch (e) {
 50 // We exceeded stack space, now return up the stack until we can execute a simple eval.
 51 // Then run an eval test to exceed stack.
 52 return -49;
3953 }
 54 } else if (depth != 0)
 55 return probeAndRecurse(depth+1);
 56
 57 try {
 58 testEval((depth > 0) ? 20 : 0);
4059 } catch (e) {
 60 testPassed("Exception: " + e);
4161 }
4262
4363 return 1;
4464}
4565
46 probeAndRecurse(0);
 66var depth = probeAndRecurse(1);
 67
 68// Tier up the eval'ed code.
 69// When run with run-jsc-stress-tests and it's agressive options, this low of a count will
 70// allow us to get up to the DFG.
 71for (var i = 0; i < 200; i++)
 72 testEval(0);
 73
 74probeAndRecurse(-depth);
180350