1/*
2 * Copyright (C) 2006, 2007, 2013, 2016 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "JSContextRef.h"
28#include "JSContextRefInternal.h"
29
30#include "APICast.h"
31#include "CallFrame.h"
32#include "InitializeThreading.h"
33#include "JSAPIGlobalObject.h"
34#include "JSCallbackObject.h"
35#include "JSClassRef.h"
36#include "JSObject.h"
37#include "JSCInlines.h"
38#include "SourceProvider.h"
39#include "StackVisitor.h"
40#include "Watchdog.h"
41#include <wtf/text/StringBuilder.h>
42#include <wtf/text/StringHash.h>
43
44#if ENABLE(REMOTE_INSPECTOR)
45#include "JSGlobalObjectDebuggable.h"
46#include "JSGlobalObjectInspectorController.h"
47#include "JSRemoteInspector.h"
48#endif
49
50#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
51#include "JSContextRefInspectorSupport.h"
52#endif
53
54#if OS(DARWIN)
55#include <mach-o/dyld.h>
56
57static const int32_t webkitFirstVersionWithConcurrentGlobalContexts = 0x2100500; // 528.5.0
58#endif
59
60using namespace JSC;
61
62// From the API's perspective, a context group remains alive iff
63// (a) it has been JSContextGroupRetained
64// OR
65// (b) one of its contexts has been JSContextRetained
66
67JSContextGroupRef JSContextGroupCreate()
68{
69 initializeThreading();
70 return toRef(&VM::createContextGroup().leakRef());
71}
72
73JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group)
74{
75 toJS(group)->ref();
76 return group;
77}
78
79void JSContextGroupRelease(JSContextGroupRef group)
80{
81 VM& vm = *toJS(group);
82
83 JSLockHolder locker(&vm);
84 vm.deref();
85}
86
87static bool internalScriptTimeoutCallback(ExecState* exec, void* callbackPtr, void* callbackData)
88{
89 JSShouldTerminateCallback callback = reinterpret_cast<JSShouldTerminateCallback>(callbackPtr);
90 JSContextRef contextRef = toRef(exec);
91 ASSERT(callback);
92 return callback(contextRef, callbackData);
93}
94
95void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef group, double limit, JSShouldTerminateCallback callback, void* callbackData)
96{
97 VM& vm = *toJS(group);
98 JSLockHolder locker(&vm);
99 Watchdog& watchdog = vm.ensureWatchdog();
100 if (callback) {
101 void* callbackPtr = reinterpret_cast<void*>(callback);
102 watchdog.setTimeLimit(Seconds { limit }, internalScriptTimeoutCallback, callbackPtr, callbackData);
103 } else
104 watchdog.setTimeLimit(Seconds { limit });
105}
106
107void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group)
108{
109 VM& vm = *toJS(group);
110 JSLockHolder locker(&vm);
111 if (vm.watchdog())
112 vm.watchdog()->setTimeLimit(Watchdog::noTimeLimit);
113}
114
115// From the API's perspective, a global context remains alive iff it has been JSGlobalContextRetained.
116
117JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
118{
119 initializeThreading();
120
121#if OS(DARWIN)
122 // If the application was linked before JSGlobalContextCreate was changed to use a unique VM,
123 // we use a shared one for backwards compatibility.
124 if (NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitFirstVersionWithConcurrentGlobalContexts) {
125 return JSGlobalContextCreateInGroup(toRef(&VM::sharedInstance()), globalObjectClass);
126 }
127#endif // OS(DARWIN)
128
129 return JSGlobalContextCreateInGroup(0, globalObjectClass);
130}
131
132JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClassRef globalObjectClass)
133{
134 initializeThreading();
135
136 Ref<VM> vm = group ? Ref<VM>(*toJS(group)) : VM::createContextGroup();
137
138 JSLockHolder locker(vm.ptr());
139
140 if (!globalObjectClass) {
141 JSGlobalObject* globalObject = JSAPIGlobalObject::create(vm.get(), JSAPIGlobalObject::createStructure(vm.get(), jsNull()));
142#if ENABLE(REMOTE_INSPECTOR)
143 if (JSRemoteInspectorGetInspectionEnabledByDefault())
144 globalObject->setRemoteDebuggingEnabled(true);
145#endif
146 return JSGlobalContextRetain(toGlobalRef(globalObject->globalExec()));
147 }
148
149 JSGlobalObject* globalObject = JSCallbackObject<JSGlobalObject>::create(vm.get(), globalObjectClass, JSCallbackObject<JSGlobalObject>::createStructure(vm.get(), 0, jsNull()));
150 ExecState* exec = globalObject->globalExec();
151 JSValue prototype = globalObjectClass->prototype(exec);
152 if (!prototype)
153 prototype = jsNull();
154 globalObject->resetPrototype(vm.get(), prototype);
155#if ENABLE(REMOTE_INSPECTOR)
156 if (JSRemoteInspectorGetInspectionEnabledByDefault())
157 globalObject->setRemoteDebuggingEnabled(true);
158#endif
159 return JSGlobalContextRetain(toGlobalRef(exec));
160}
161
162JSGlobalContextRef JSGlobalContextRetain(JSGlobalContextRef ctx)
163{
164 ExecState* exec = toJS(ctx);
165 VM& vm = exec->vm();
166 JSLockHolder locker(vm);
167
168 gcProtect(vm.vmEntryGlobalObject(exec));
169 vm.ref();
170 return ctx;
171}
172
173void JSGlobalContextRelease(JSGlobalContextRef ctx)
174{
175 ExecState* exec = toJS(ctx);
176 VM& vm = exec->vm();
177 JSLockHolder locker(vm);
178
179 bool protectCountIsZero = vm.heap.unprotect(vm.vmEntryGlobalObject(exec));
180 if (protectCountIsZero)
181 vm.heap.reportAbandonedObjectGraph();
182 vm.deref();
183}
184
185JSObjectRef JSContextGetGlobalObject(JSContextRef ctx)
186{
187 if (!ctx) {
188 ASSERT_NOT_REACHED();
189 return 0;
190 }
191 ExecState* exec = toJS(ctx);
192 VM& vm = exec->vm();
193 JSLockHolder locker(vm);
194
195 return toRef(jsCast<JSObject*>(exec->lexicalGlobalObject()->methodTable(vm)->toThis(exec->lexicalGlobalObject(), exec, NotStrictMode)));
196}
197
198JSContextGroupRef JSContextGetGroup(JSContextRef ctx)
199{
200 if (!ctx) {
201 ASSERT_NOT_REACHED();
202 return 0;
203 }
204 ExecState* exec = toJS(ctx);
205 return toRef(&exec->vm());
206}
207
208JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx)
209{
210 if (!ctx) {
211 ASSERT_NOT_REACHED();
212 return 0;
213 }
214 ExecState* exec = toJS(ctx);
215 JSLockHolder locker(exec);
216
217 return toGlobalRef(exec->lexicalGlobalObject()->globalExec());
218}
219
220JSStringRef JSGlobalContextCopyName(JSGlobalContextRef ctx)
221{
222 if (!ctx) {
223 ASSERT_NOT_REACHED();
224 return 0;
225 }
226
227 ExecState* exec = toJS(ctx);
228 VM& vm = exec->vm();
229 JSLockHolder locker(vm);
230
231 String name = vm.vmEntryGlobalObject(exec)->name();
232 if (name.isNull())
233 return 0;
234
235 return OpaqueJSString::tryCreate(name).leakRef();
236}
237
238void JSGlobalContextSetName(JSGlobalContextRef ctx, JSStringRef name)
239{
240 if (!ctx) {
241 ASSERT_NOT_REACHED();
242 return;
243 }
244
245 ExecState* exec = toJS(ctx);
246 VM& vm = exec->vm();
247 JSLockHolder locker(vm);
248
249 vm.vmEntryGlobalObject(exec)->setName(name ? name->string() : String());
250}
251
252
253class BacktraceFunctor {
254public:
255 BacktraceFunctor(StringBuilder& builder, unsigned remainingCapacityForFrameCapture)
256 : m_builder(builder)
257 , m_remainingCapacityForFrameCapture(remainingCapacityForFrameCapture)
258 {
259 }
260
261 StackVisitor::Status operator()(StackVisitor& visitor) const
262 {
263 if (m_remainingCapacityForFrameCapture) {
264 // If callee is unknown, but we've not added any frame yet, we should
265 // still add the frame, because something called us, and gave us arguments.
266 if (visitor->callee().isCell()) {
267 JSCell* callee = visitor->callee().asCell();
268 if (!callee && visitor->index())
269 return StackVisitor::Done;
270 }
271
272 StringBuilder& builder = m_builder;
273 if (!builder.isEmpty())
274 builder.append('\n');
275 builder.append('#');
276 builder.appendNumber(visitor->index());
277 builder.append(' ');
278 builder.append(visitor->functionName());
279 builder.appendLiteral("() at ");
280 builder.append(visitor->sourceURL());
281 if (visitor->hasLineAndColumnInfo()) {
282 builder.append(':');
283 unsigned lineNumber;
284 unsigned unusedColumn;
285 visitor->computeLineAndColumn(lineNumber, unusedColumn);
286 builder.appendNumber(lineNumber);
287 }
288
289 if (!visitor->callee().rawPtr())
290 return StackVisitor::Done;
291
292 m_remainingCapacityForFrameCapture--;
293 return StackVisitor::Continue;
294 }
295 return StackVisitor::Done;
296 }
297
298private:
299 StringBuilder& m_builder;
300 mutable unsigned m_remainingCapacityForFrameCapture;
301};
302
303JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize)
304{
305 if (!ctx) {
306 ASSERT_NOT_REACHED();
307 return 0;
308 }
309 ExecState* exec = toJS(ctx);
310 VM& vm = exec->vm();
311 JSLockHolder lock(vm);
312 StringBuilder builder;
313 CallFrame* frame = vm.topCallFrame;
314
315 ASSERT(maxStackSize);
316 BacktraceFunctor functor(builder, maxStackSize);
317 frame->iterate(functor);
318
319 return OpaqueJSString::tryCreate(builder.toString()).leakRef();
320}
321
322bool JSGlobalContextGetRemoteInspectionEnabled(JSGlobalContextRef ctx)
323{
324 if (!ctx) {
325 ASSERT_NOT_REACHED();
326 return false;
327 }
328
329 ExecState* exec = toJS(ctx);
330 VM& vm = exec->vm();
331 JSLockHolder lock(vm);
332
333 return vm.vmEntryGlobalObject(exec)->remoteDebuggingEnabled();
334}
335
336void JSGlobalContextSetRemoteInspectionEnabled(JSGlobalContextRef ctx, bool enabled)
337{
338 if (!ctx) {
339 ASSERT_NOT_REACHED();
340 return;
341 }
342
343 ExecState* exec = toJS(ctx);
344 VM& vm = exec->vm();
345 JSLockHolder lock(vm);
346
347 vm.vmEntryGlobalObject(exec)->setRemoteDebuggingEnabled(enabled);
348}
349
350bool JSGlobalContextGetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx)
351{
352#if ENABLE(REMOTE_INSPECTOR)
353 if (!ctx) {
354 ASSERT_NOT_REACHED();
355 return false;
356 }
357
358 ExecState* exec = toJS(ctx);
359 VM& vm = exec->vm();
360 JSLockHolder lock(vm);
361
362 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
363 return globalObject->inspectorController().includesNativeCallStackWhenReportingExceptions();
364#else
365 UNUSED_PARAM(ctx);
366 return false;
367#endif
368}
369
370void JSGlobalContextSetIncludesNativeCallStackWhenReportingExceptions(JSGlobalContextRef ctx, bool includesNativeCallStack)
371{
372#if ENABLE(REMOTE_INSPECTOR)
373 if (!ctx) {
374 ASSERT_NOT_REACHED();
375 return;
376 }
377
378 ExecState* exec = toJS(ctx);
379 VM& vm = exec->vm();
380 JSLockHolder lock(vm);
381
382 JSGlobalObject* globalObject = vm.vmEntryGlobalObject(exec);
383 globalObject->inspectorController().setIncludesNativeCallStackWhenReportingExceptions(includesNativeCallStack);
384#else
385 UNUSED_PARAM(ctx);
386 UNUSED_PARAM(includesNativeCallStack);
387#endif
388}
389
390#if USE(CF)
391CFRunLoopRef JSGlobalContextGetDebuggerRunLoop(JSGlobalContextRef ctx)
392{
393#if ENABLE(REMOTE_INSPECTOR)
394 if (!ctx) {
395 ASSERT_NOT_REACHED();
396 return nullptr;
397 }
398
399 ExecState* exec = toJS(ctx);
400 VM& vm = exec->vm();
401 JSLockHolder lock(vm);
402
403 return vm.vmEntryGlobalObject(exec)->inspectorDebuggable().targetRunLoop();
404#else
405 UNUSED_PARAM(ctx);
406 return nullptr;
407#endif
408}
409
410void JSGlobalContextSetDebuggerRunLoop(JSGlobalContextRef ctx, CFRunLoopRef runLoop)
411{
412#if ENABLE(REMOTE_INSPECTOR)
413 if (!ctx) {
414 ASSERT_NOT_REACHED();
415 return;
416 }
417
418 ExecState* exec = toJS(ctx);
419 VM& vm = exec->vm();
420 JSLockHolder lock(vm);
421
422 vm.vmEntryGlobalObject(exec)->inspectorDebuggable().setTargetRunLoop(runLoop);
423#else
424 UNUSED_PARAM(ctx);
425 UNUSED_PARAM(runLoop);
426#endif
427}
428#endif // USE(CF)
429
430#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS)
431Inspector::AugmentableInspectorController* JSGlobalContextGetAugmentableInspectorController(JSGlobalContextRef ctx)
432{
433 if (!ctx) {
434 ASSERT_NOT_REACHED();
435 return nullptr;
436 }
437
438 ExecState* exec = toJS(ctx);
439 VM& vm = exec->vm();
440 JSLockHolder lock(vm);
441
442 return &vm.vmEntryGlobalObject(exec)->inspectorController();
443}
444#endif
445