[JSC] Build testapi in non Apple ports
[WebKit-https.git] / Source / JavaScriptCore / API / tests / PingPongStackOverflowTest.cpp
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PingPongStackOverflowTest.h"
28
29 #include "InitializeThreading.h"
30 #include "JSContextRefPrivate.h"
31 #include "JavaScript.h"
32 #include "Options.h"
33 #include <wtf/text/StringBuilder.h>
34
35 using JSC::Options;
36
37 static JSGlobalContextRef context = nullptr;
38 static int nativeRecursionCount = 0;
39
40 static bool PingPongStackOverflowObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
41 {
42     UNUSED_PARAM(context);
43     UNUSED_PARAM(constructor);
44
45     JSStringRef hasInstanceName = JSStringCreateWithUTF8CString("hasInstance");
46     JSValueRef hasInstance = JSObjectGetProperty(context, constructor, hasInstanceName, exception);
47     JSStringRelease(hasInstanceName);
48     if (!hasInstance)
49         return false;
50
51     int countAtEntry = nativeRecursionCount++;
52
53     JSValueRef result = 0;
54     if (nativeRecursionCount < 100) {
55         JSObjectRef function = JSValueToObject(context, hasInstance, exception);
56         result = JSObjectCallAsFunction(context, function, constructor, 1, &possibleValue, exception);
57     } else {
58         StringBuilder builder;
59         builder.appendLiteral("dummy.valueOf([0]");
60         for (int i = 1; i < 35000; i++) {
61             builder.appendLiteral(", [");
62             builder.appendNumber(i);
63             builder.appendLiteral("]");
64         }
65         builder.appendLiteral(");");
66
67         JSStringRef script = JSStringCreateWithUTF8CString(builder.toString().utf8().data());
68         result = JSEvaluateScript(context, script, NULL, NULL, 1, exception);
69         JSStringRelease(script);
70     }
71
72     --nativeRecursionCount;
73     if (nativeRecursionCount != countAtEntry)
74         printf("    ERROR: PingPongStackOverflow test saw a recursion count mismatch\n");
75
76     return result && JSValueToBoolean(context, result);
77 }
78
79 JSClassDefinition PingPongStackOverflowObject_definition = {
80     0,
81     kJSClassAttributeNone,
82     
83     "PingPongStackOverflowObject",
84     NULL,
85     
86     NULL,
87     NULL,
88     
89     NULL,
90     NULL,
91     NULL,
92     NULL,
93     NULL,
94     NULL,
95     NULL,
96     NULL,
97     NULL,
98     PingPongStackOverflowObject_hasInstance,
99     NULL,
100 };
101
102 static JSClassRef PingPongStackOverflowObject_class(JSContextRef context)
103 {
104     UNUSED_PARAM(context);
105     
106     static JSClassRef jsClass;
107     if (!jsClass)
108         jsClass = JSClassCreate(&PingPongStackOverflowObject_definition);
109     
110     return jsClass;
111 }
112
113 // This tests tests a stack overflow on VM reentry into a JS function from a native function
114 // after ping-pong'ing back and forth between JS and native functions multiple times.
115 // This test should not hang or crash.
116 int testPingPongStackOverflow()
117 {
118     bool failed = false;
119
120     JSC::initializeThreading();
121     Options::initialize(); // Ensure options is initialized first.
122
123     auto origSoftReservedZoneSize = Options::softReservedZoneSize();
124     auto origReservedZoneSize = Options::reservedZoneSize();
125     auto origUseLLInt = Options::useLLInt();
126     auto origMaxPerThreadStackUsage = Options::maxPerThreadStackUsage();
127
128     Options::softReservedZoneSize() = 128 * KB;
129     Options::reservedZoneSize() = 64 * KB;
130 #if ENABLE(JIT)
131     // Normally, we want to disable the LLINT to force the use of JITted code which is necessary for
132     // reproducing the regression in https://bugs.webkit.org/show_bug.cgi?id=148749. However, we only
133     // want to do this if the LLINT isn't the only available execution engine.
134     Options::useLLInt() = false;
135 #endif
136
137     const char* scriptString =
138         "var count = 0;" \
139         "PingPongStackOverflowObject.hasInstance = function f() {" \
140         "    return (undefined instanceof PingPongStackOverflowObject);" \
141         "};" \
142         "PingPongStackOverflowObject.__proto__ = undefined;" \
143         "undefined instanceof PingPongStackOverflowObject;";
144
145     JSValueRef scriptResult = nullptr;
146     JSValueRef exception = nullptr;
147     JSStringRef script = JSStringCreateWithUTF8CString(scriptString);
148
149     nativeRecursionCount = 0;
150     context = JSGlobalContextCreateInGroup(nullptr, nullptr);
151
152     JSObjectRef globalObject = JSContextGetGlobalObject(context);
153     ASSERT(JSValueIsObject(context, globalObject));
154
155     JSObjectRef PingPongStackOverflowObject = JSObjectMake(context, PingPongStackOverflowObject_class(context), NULL);
156     JSStringRef PingPongStackOverflowObjectString = JSStringCreateWithUTF8CString("PingPongStackOverflowObject");
157     JSObjectSetProperty(context, globalObject, PingPongStackOverflowObjectString, PingPongStackOverflowObject, kJSPropertyAttributeNone, NULL);
158     JSStringRelease(PingPongStackOverflowObjectString);
159
160     unsigned stackSize = 32 * KB;
161     Options::maxPerThreadStackUsage() = stackSize + Options::softReservedZoneSize();
162
163     exception = nullptr;
164     scriptResult = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
165
166     if (!exception) {
167         printf("FAIL: PingPongStackOverflowError not thrown in PingPongStackOverflow test\n");
168         failed = true;
169     } else if (nativeRecursionCount) {
170         printf("FAIL: Unbalanced native recursion count: %d in PingPongStackOverflow test\n", nativeRecursionCount);
171         failed = true;
172     } else {
173         printf("PASS: PingPongStackOverflow test.\n");
174     }
175
176     Options::softReservedZoneSize() = origSoftReservedZoneSize;
177     Options::reservedZoneSize() = origReservedZoneSize;
178     Options::useLLInt() = origUseLLInt;
179     Options::maxPerThreadStackUsage() = origMaxPerThreadStackUsage;
180
181     return failed;
182 }