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