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 | |
59 | static constexpr int32_t webkitFirstVersionWithConcurrentGlobalContexts = 0x2100500; // 528.5.0 |
60 | #endif |
61 | |
62 | using 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 | |
69 | JSContextGroupRef JSContextGroupCreate() |
70 | { |
71 | initializeThreading(); |
72 | return toRef(&VM::createContextGroup().leakRef()); |
73 | } |
74 | |
75 | JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group) |
76 | { |
77 | toJS(group)->ref(); |
78 | return group; |
79 | } |
80 | |
81 | void JSContextGroupRelease(JSContextGroupRef group) |
82 | { |
83 | VM& vm = *toJS(group); |
84 | |
85 | JSLockHolder locker(&vm); |
86 | vm.deref(); |
87 | } |
88 | |
89 | static 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 | |
97 | void 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 | |
109 | void 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 | |
119 | JSGlobalContextRef 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 | |
134 | JSGlobalContextRef 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 | |
163 | JSGlobalContextRef 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 | |
174 | void 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 | |
186 | JSObjectRef 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 | |
199 | JSContextGroupRef 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 | |
209 | JSGlobalContextRef 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 | |
221 | JSStringRef 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 | |
239 | void 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 | |
253 | void 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 | |
273 | class BacktraceFunctor { |
274 | public: |
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 | |
318 | private: |
319 | StringBuilder& m_builder; |
320 | mutable unsigned m_remainingCapacityForFrameCapture; |
321 | }; |
322 | |
323 | JSStringRef 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 | |
342 | bool 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 | |
356 | void 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 | |
370 | bool 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 | |
389 | void 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) |
409 | CFRunLoopRef 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 | |
428 | void 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) |
449 | Inspector::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 | |