2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ExecutionTimeLimitTest.h"
31 #include "JSContextRefPrivate.h"
32 #include "JavaScriptCore.h"
34 #include <mach/mach.h>
35 #include <mach/mach_time.h>
39 static JSGlobalContextRef context
= nullptr;
41 static double currentCPUTime()
43 mach_msg_type_number_t infoCount
= THREAD_BASIC_INFO_COUNT
;
44 thread_basic_info_data_t info
;
46 /* Get thread information */
47 mach_port_t threadPort
= mach_thread_self();
48 thread_info(threadPort
, THREAD_BASIC_INFO
, (thread_info_t
)(&info
), &infoCount
);
49 mach_port_deallocate(mach_task_self(), threadPort
);
51 double time
= info
.user_time
.seconds
+ info
.user_time
.microseconds
/ 1000000.;
52 time
+= info
.system_time
.seconds
+ info
.system_time
.microseconds
/ 1000000.;
57 static JSValueRef
currentCPUTimeAsJSFunctionCallback(JSContextRef ctx
, JSObjectRef functionObject
, JSObjectRef thisObject
, size_t argumentCount
, const JSValueRef arguments
[], JSValueRef
* exception
)
59 UNUSED_PARAM(functionObject
);
60 UNUSED_PARAM(thisObject
);
61 UNUSED_PARAM(argumentCount
);
62 UNUSED_PARAM(arguments
);
63 UNUSED_PARAM(exception
);
65 ASSERT(JSContextGetGlobalContext(ctx
) == context
);
66 return JSValueMakeNumber(ctx
, currentCPUTime());
69 bool shouldTerminateCallbackWasCalled
= false;
70 static bool shouldTerminateCallback(JSContextRef ctx
, void* context
)
73 UNUSED_PARAM(context
);
74 shouldTerminateCallbackWasCalled
= true;
78 bool cancelTerminateCallbackWasCalled
= false;
79 static bool cancelTerminateCallback(JSContextRef ctx
, void* context
)
82 UNUSED_PARAM(context
);
83 cancelTerminateCallbackWasCalled
= true;
87 int extendTerminateCallbackCalled
= 0;
88 static bool extendTerminateCallback(JSContextRef ctx
, void* context
)
90 UNUSED_PARAM(context
);
91 extendTerminateCallbackCalled
++;
92 if (extendTerminateCallbackCalled
== 1) {
93 JSContextGroupRef contextGroup
= JSContextGetGroup(ctx
);
94 JSContextGroupSetExecutionTimeLimit(contextGroup
, .200f
, extendTerminateCallback
, 0);
101 int testExecutionTimeLimit()
103 context
= JSGlobalContextCreateInGroup(nullptr, nullptr);
105 JSContextGroupRef contextGroup
= JSContextGetGroup(context
);
106 JSObjectRef globalObject
= JSContextGetGlobalObject(context
);
107 ASSERT(JSValueIsObject(context
, globalObject
));
109 JSValueRef v
= nullptr;
110 JSValueRef exception
= nullptr;
113 JSStringRef currentCPUTimeStr
= JSStringCreateWithUTF8CString("currentCPUTime");
114 JSObjectRef currentCPUTimeFunction
= JSObjectMakeFunctionWithCallback(context
, currentCPUTimeStr
, currentCPUTimeAsJSFunctionCallback
);
115 JSObjectSetProperty(context
, globalObject
, currentCPUTimeStr
, currentCPUTimeFunction
, kJSPropertyAttributeNone
, nullptr);
116 JSStringRelease(currentCPUTimeStr
);
118 /* Test script timeout: */
119 JSContextGroupSetExecutionTimeLimit(contextGroup
, .10f
, shouldTerminateCallback
, 0);
121 const char* loopForeverScript
= "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
122 JSStringRef script
= JSStringCreateWithUTF8CString(loopForeverScript
);
126 shouldTerminateCallbackWasCalled
= false;
127 startTime
= currentCPUTime();
128 v
= JSEvaluateScript(context
, script
, nullptr, nullptr, 1, &exception
);
129 endTime
= currentCPUTime();
131 if (((endTime
- startTime
) < .150f
) && shouldTerminateCallbackWasCalled
)
132 printf("PASS: script timed out as expected.\n");
134 if (!((endTime
- startTime
) < .150f
))
135 printf("FAIL: script did not time out as expected.\n");
136 if (!shouldTerminateCallbackWasCalled
)
137 printf("FAIL: script timeout callback was not called.\n");
142 printf("FAIL: TerminatedExecutionException was not thrown.\n");
147 /* Test the script timeout's TerminatedExecutionException should NOT be catchable: */
148 JSContextGroupSetExecutionTimeLimit(contextGroup
, 0.10f
, shouldTerminateCallback
, 0);
150 const char* loopForeverScript
= "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
151 JSStringRef script
= JSStringCreateWithUTF8CString(loopForeverScript
);
155 shouldTerminateCallbackWasCalled
= false;
156 startTime
= currentCPUTime();
157 v
= JSEvaluateScript(context
, script
, nullptr, nullptr, 1, &exception
);
158 endTime
= currentCPUTime();
160 if (((endTime
- startTime
) >= .150f
) || !shouldTerminateCallbackWasCalled
) {
161 if (!((endTime
- startTime
) < .150f
))
162 printf("FAIL: script did not time out as expected.\n");
163 if (!shouldTerminateCallbackWasCalled
)
164 printf("FAIL: script timeout callback was not called.\n");
169 printf("PASS: TerminatedExecutionException was not catchable as expected.\n");
171 printf("FAIL: TerminatedExecutionException was caught.\n");
176 /* Test script timeout with no callback: */
177 JSContextGroupSetExecutionTimeLimit(contextGroup
, .10f
, 0, 0);
179 const char* loopForeverScript
= "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
180 JSStringRef script
= JSStringCreateWithUTF8CString(loopForeverScript
);
184 startTime
= currentCPUTime();
185 v
= JSEvaluateScript(context
, script
, nullptr, nullptr, 1, &exception
);
186 endTime
= currentCPUTime();
188 if (((endTime
- startTime
) < .150f
) && shouldTerminateCallbackWasCalled
)
189 printf("PASS: script timed out as expected when no callback is specified.\n");
191 if (!((endTime
- startTime
) < .150f
))
192 printf("FAIL: script did not time out as expected when no callback is specified.\n");
197 printf("FAIL: TerminatedExecutionException was not thrown.\n");
202 /* Test script timeout cancellation: */
203 JSContextGroupSetExecutionTimeLimit(contextGroup
, 0.10f
, cancelTerminateCallback
, 0);
205 const char* loopForeverScript
= "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
206 JSStringRef script
= JSStringCreateWithUTF8CString(loopForeverScript
);
210 startTime
= currentCPUTime();
211 v
= JSEvaluateScript(context
, script
, nullptr, nullptr, 1, &exception
);
212 endTime
= currentCPUTime();
214 if (((endTime
- startTime
) >= .150f
) && cancelTerminateCallbackWasCalled
&& !exception
)
215 printf("PASS: script timeout was cancelled as expected.\n");
217 if (((endTime
- startTime
) < .150) || exception
)
218 printf("FAIL: script timeout was not cancelled.\n");
219 if (!cancelTerminateCallbackWasCalled
)
220 printf("FAIL: script timeout callback was not called.\n");
225 printf("FAIL: Unexpected TerminatedExecutionException thrown.\n");
230 /* Test script timeout extension: */
231 JSContextGroupSetExecutionTimeLimit(contextGroup
, 0.100f
, extendTerminateCallback
, 0);
233 const char* loopForeverScript
= "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
234 JSStringRef script
= JSStringCreateWithUTF8CString(loopForeverScript
);
239 startTime
= currentCPUTime();
240 v
= JSEvaluateScript(context
, script
, nullptr, nullptr, 1, &exception
);
241 endTime
= currentCPUTime();
242 deltaTime
= endTime
- startTime
;
244 if ((deltaTime
>= .300f
) && (deltaTime
< .500f
) && (extendTerminateCallbackCalled
== 2) && exception
)
245 printf("PASS: script timeout was extended as expected.\n");
247 if (deltaTime
< .200f
)
248 printf("FAIL: script timeout was not extended as expected.\n");
249 else if (deltaTime
>= .500f
)
250 printf("FAIL: script did not timeout.\n");
252 if (extendTerminateCallbackCalled
< 1)
253 printf("FAIL: script timeout callback was not called.\n");
254 if (extendTerminateCallbackCalled
< 2)
255 printf("FAIL: script timeout callback was not called after timeout extension.\n");
258 printf("FAIL: TerminatedExecutionException was not thrown during timeout extension test.\n");
264 JSGlobalContextRelease(context
);