1/*
2 * Copyright (C) 2006, 2007 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 COMPUTER, 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 COMPUTER, 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 "JSContextRefPrivate.h"
29
30#include "APICast.h"
31#include "InitializeThreading.h"
32#include <interpreter/CallFrame.h>
33#include <interpreter/Interpreter.h>
34#include "JSCallbackObject.h"
35#include "JSClassRef.h"
36#include "JSGlobalObject.h"
37#include "JSObject.h"
38#include "Operations.h"
39#include "SourceProvider.h"
40#include <wtf/text/StringBuilder.h>
41#include <wtf/text/StringHash.h>
42
43#if OS(DARWIN)
44#include <mach-o/dyld.h>
45
46static const int32_t webkitFirstVersionWithConcurrentGlobalContexts = 0x2100500; // 528.5.0
47#endif
48
49using namespace JSC;
50
51// From the API's perspective, a context group remains alive iff
52//     (a) it has been JSContextGroupRetained
53//     OR
54//     (b) one of its contexts has been JSContextRetained
55
56JSContextGroupRef JSContextGroupCreate()
57{
58    initializeThreading();
59    return toRef(VM::createContextGroup().leakRef());
60}
61
62JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group)
63{
64    toJS(group)->ref();
65    return group;
66}
67
68void JSContextGroupRelease(JSContextGroupRef group)
69{
70    IdentifierTable* savedIdentifierTable;
71    VM& vm = *toJS(group);
72
73    {
74        JSLockHolder lock(vm);
75        savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
76        vm.deref();
77    }
78
79    wtfThreadData().setCurrentIdentifierTable(savedIdentifierTable);
80}
81
82static bool internalScriptTimeoutCallback(ExecState* exec, void* callbackPtr, void* callbackData)
83{
84    JSShouldTerminateCallback callback = reinterpret_cast<JSShouldTerminateCallback>(callbackPtr);
85    JSContextRef contextRef = toRef(exec);
86    ASSERT(callback);
87    return callback(contextRef, callbackData);
88}
89
90void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef group, double limit, JSShouldTerminateCallback callback, void* callbackData)
91{
92    VM& vm = *toJS(group);
93    APIEntryShim entryShim(&vm);
94    Watchdog& watchdog = vm.watchdog;
95    if (callback) {
96        void* callbackPtr = reinterpret_cast<void*>(callback);
97        watchdog.setTimeLimit(vm, limit, internalScriptTimeoutCallback, callbackPtr, callbackData);
98    } else
99        watchdog.setTimeLimit(vm, limit);
100}
101
102void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group)
103{
104    VM& vm = *toJS(group);
105    APIEntryShim entryShim(&vm);
106    Watchdog& watchdog = vm.watchdog;
107    watchdog.setTimeLimit(vm, std::numeric_limits<double>::infinity());
108}
109
110// From the API's perspective, a global context remains alive iff it has been JSGlobalContextRetained.
111
112JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
113{
114    initializeThreading();
115
116#if OS(DARWIN)
117    // If the application was linked before JSGlobalContextCreate was changed to use a unique VM,
118    // we use a shared one for backwards compatibility.
119    if (NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitFirstVersionWithConcurrentGlobalContexts) {
120        return JSGlobalContextCreateInGroup(toRef(&VM::sharedInstance()), globalObjectClass);
121    }
122#endif // OS(DARWIN)
123
124    return JSGlobalContextCreateInGroup(0, globalObjectClass);
125}
126
127JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClassRef globalObjectClass)
128{
129    initializeThreading();
130
131    RefPtr<VM> vm = group ? PassRefPtr<VM>(toJS(group)) : VM::createContextGroup();
132
133    APIEntryShim entryShim(vm.get(), false);
134    vm->makeUsableFromMultipleThreads();
135
136    if (!globalObjectClass) {
137        JSGlobalObject* globalObject = JSGlobalObject::create(*vm, JSGlobalObject::createStructure(*vm, jsNull()));
138        return JSGlobalContextRetain(toGlobalRef(globalObject->globalExec()));
139    }
140
141    JSGlobalObject* globalObject = JSCallbackObject<JSGlobalObject>::create(*vm, globalObjectClass, JSCallbackObject<JSGlobalObject>::createStructure(*vm, 0, jsNull()));
142    ExecState* exec = globalObject->globalExec();
143    JSValue prototype = globalObjectClass->prototype(exec);
144    if (!prototype)
145        prototype = jsNull();
146    globalObject->resetPrototype(*vm, prototype);
147    return JSGlobalContextRetain(toGlobalRef(exec));
148}
149
150JSGlobalContextRef JSGlobalContextRetain(JSGlobalContextRef ctx)
151{
152    ExecState* exec = toJS(ctx);
153    APIEntryShim entryShim(exec);
154
155    VM& vm = exec->vm();
156    gcProtect(exec->dynamicGlobalObject());
157    vm.ref();
158    return ctx;
159}
160
161void JSGlobalContextRelease(JSGlobalContextRef ctx)
162{
163    IdentifierTable* savedIdentifierTable;
164    ExecState* exec = toJS(ctx);
165    {
166        JSLockHolder lock(exec);
167
168        VM& vm = exec->vm();
169        savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
170
171        bool protectCountIsZero = Heap::heap(exec->dynamicGlobalObject())->unprotect(exec->dynamicGlobalObject());
172        if (protectCountIsZero)
173            vm.heap.reportAbandonedObjectGraph();
174        vm.deref();
175    }
176
177    wtfThreadData().setCurrentIdentifierTable(savedIdentifierTable);
178}
179
180JSObjectRef JSContextGetGlobalObject(JSContextRef ctx)
181{
182    if (!ctx) {
183        ASSERT_NOT_REACHED();
184        return 0;
185    }
186    ExecState* exec = toJS(ctx);
187    APIEntryShim entryShim(exec);
188
189    // It is necessary to call toThisObject to get the wrapper object when used with WebCore.
190    return toRef(exec->lexicalGlobalObject()->methodTable()->toThisObject(exec->lexicalGlobalObject(), exec));
191}
192
193JSContextGroupRef JSContextGetGroup(JSContextRef ctx)
194{
195    if (!ctx) {
196        ASSERT_NOT_REACHED();
197        return 0;
198    }
199    ExecState* exec = toJS(ctx);
200    return toRef(&exec->vm());
201}
202
203JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx)
204{
205    if (!ctx) {
206        ASSERT_NOT_REACHED();
207        return 0;
208    }
209    ExecState* exec = toJS(ctx);
210    APIEntryShim entryShim(exec);
211
212    return toGlobalRef(exec->lexicalGlobalObject()->globalExec());
213}
214
215JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize)
216{
217    if (!ctx) {
218        ASSERT_NOT_REACHED();
219        return 0;
220    }
221    ExecState* exec = toJS(ctx);
222    JSLockHolder lock(exec);
223    StringBuilder builder;
224    Vector<StackFrame> stackTrace;
225    Interpreter::getStackTrace(&exec->vm(), stackTrace, maxStackSize);
226
227    for (size_t i = 0; i < stackTrace.size(); i++) {
228        String urlString;
229        String functionName;
230        StackFrame& frame = stackTrace[i];
231        JSValue function = frame.callee.get();
232        if (frame.callee)
233            functionName = frame.friendlyFunctionName(exec);
234        else {
235            // Caller is unknown, but if frame is empty we should still add the frame, because
236            // something called us, and gave us arguments.
237            if (i)
238                break;
239        }
240        unsigned lineNumber;
241        unsigned column;
242        frame.computeLineAndColumn(lineNumber, column);
243        if (!builder.isEmpty())
244            builder.append('\n');
245        builder.append('#');
246        builder.appendNumber(i);
247        builder.append(' ');
248        builder.append(functionName);
249        builder.appendLiteral("() at ");
250        builder.append(urlString);
251        if (frame.codeType != StackFrameNativeCode) {
252            builder.append(':');
253            builder.appendNumber(lineNumber);
254        }
255        if (!function)
256            break;
257    }
258    return OpaqueJSString::create(builder.toString()).leakRef();
259}
260
261
262