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 | |
57 | static const int32_t webkitFirstVersionWithConcurrentGlobalContexts = 0x2100500; // 528.5.0 |
58 | #endif |
59 | |
60 | using 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 | |
67 | JSContextGroupRef JSContextGroupCreate() |
68 | { |
69 | initializeThreading(); |
70 | return toRef(&VM::createContextGroup().leakRef()); |
71 | } |
72 | |
73 | JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group) |
74 | { |
75 | toJS(group)->ref(); |
76 | return group; |
77 | } |
78 | |
79 | void JSContextGroupRelease(JSContextGroupRef group) |
80 | { |
81 | VM& vm = *toJS(group); |
82 | |
83 | JSLockHolder locker(&vm); |
84 | vm.deref(); |
85 | } |
86 | |
87 | static 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 | |
95 | void 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 | |
107 | void 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 | |
117 | JSGlobalContextRef 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 | |
132 | JSGlobalContextRef 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 | |
162 | JSGlobalContextRef 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 | |
173 | void 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 | |
185 | JSObjectRef 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 | |
198 | JSContextGroupRef 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 | |
208 | JSGlobalContextRef 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 | |
220 | JSStringRef 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 | |
238 | void 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 | |
253 | class BacktraceFunctor { |
254 | public: |
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 | |
298 | private: |
299 | StringBuilder& m_builder; |
300 | mutable unsigned m_remainingCapacityForFrameCapture; |
301 | }; |
302 | |
303 | JSStringRef 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 | |
322 | bool 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 | |
336 | void 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 | |
350 | bool 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 | |
370 | void 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) |
391 | CFRunLoopRef 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 | |
410 | void 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) |
431 | Inspector::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 | |