#include "APICast.h"
#include "InitializeThreading.h"
+#include <interpreter/CallFrame.h>
+#include <interpreter/Interpreter.h>
#include "JSCallbackObject.h"
#include "JSClassRef.h"
#include "JSGlobalObject.h"
#include "JSObject.h"
-#include <wtf/Platform.h>
+#include "Operations.h"
+#include "SourceProvider.h"
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringHash.h>
#if OS(DARWIN)
#include <mach-o/dyld.h>
using namespace JSC;
+// From the API's perspective, a context group remains alive iff
+// (a) it has been JSContextGroupRetained
+// OR
+// (b) one of its contexts has been JSContextRetained
+
JSContextGroupRef JSContextGroupCreate()
{
initializeThreading();
- return toRef(JSGlobalData::createNonDefault().releaseRef());
+ return toRef(VM::createContextGroup().leakRef());
}
JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group)
void JSContextGroupRelease(JSContextGroupRef group)
{
- toJS(group)->deref();
+ IdentifierTable* savedIdentifierTable;
+ VM& vm = *toJS(group);
+
+ {
+ JSLockHolder lock(vm);
+ savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
+ vm.deref();
+ }
+
+ wtfThreadData().setCurrentIdentifierTable(savedIdentifierTable);
+}
+
+static bool internalScriptTimeoutCallback(ExecState* exec, void* callbackPtr, void* callbackData)
+{
+ JSShouldTerminateCallback callback = reinterpret_cast<JSShouldTerminateCallback>(callbackPtr);
+ JSContextRef contextRef = toRef(exec);
+ ASSERT(callback);
+ return callback(contextRef, callbackData);
}
+void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef group, double limit, JSShouldTerminateCallback callback, void* callbackData)
+{
+ VM& vm = *toJS(group);
+ APIEntryShim entryShim(&vm);
+ Watchdog& watchdog = vm.watchdog;
+ if (callback) {
+ void* callbackPtr = reinterpret_cast<void*>(callback);
+ watchdog.setTimeLimit(vm, limit, internalScriptTimeoutCallback, callbackPtr, callbackData);
+ } else
+ watchdog.setTimeLimit(vm, limit);
+}
+
+void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group)
+{
+ VM& vm = *toJS(group);
+ APIEntryShim entryShim(&vm);
+ Watchdog& watchdog = vm.watchdog;
+ watchdog.setTimeLimit(vm, std::numeric_limits<double>::infinity());
+}
+
+// From the API's perspective, a global context remains alive iff it has been JSGlobalContextRetained.
+
JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
{
initializeThreading();
+
#if OS(DARWIN)
- // When running on Tiger or Leopard, or if the application was linked before JSGlobalContextCreate was changed
- // to use a unique JSGlobalData, we use a shared one for compatibility.
-#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ // If the application was linked before JSGlobalContextCreate was changed to use a unique VM,
+ // we use a shared one for backwards compatibility.
if (NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitFirstVersionWithConcurrentGlobalContexts) {
-#else
- {
-#endif
- JSLock lock(LockForReal);
- return JSGlobalContextCreateInGroup(toRef(&JSGlobalData::sharedInstance()), globalObjectClass);
+ return JSGlobalContextCreateInGroup(toRef(&VM::sharedInstance()), globalObjectClass);
}
#endif // OS(DARWIN)
{
initializeThreading();
- JSLock lock(LockForReal);
- RefPtr<JSGlobalData> globalData = group ? PassRefPtr<JSGlobalData>(toJS(group)) : JSGlobalData::createNonDefault();
+ RefPtr<VM> vm = group ? PassRefPtr<VM>(toJS(group)) : VM::createContextGroup();
- APIEntryShim entryShim(globalData.get(), false);
-
-#if ENABLE(JSC_MULTIPLE_THREADS)
- globalData->makeUsableFromMultipleThreads();
-#endif
+ APIEntryShim entryShim(vm.get(), false);
+ vm->makeUsableFromMultipleThreads();
if (!globalObjectClass) {
- JSGlobalObject* globalObject = new (globalData.get()) JSGlobalObject;
+ JSGlobalObject* globalObject = JSGlobalObject::create(*vm, JSGlobalObject::createStructure(*vm, jsNull()));
return JSGlobalContextRetain(toGlobalRef(globalObject->globalExec()));
}
- JSGlobalObject* globalObject = new (globalData.get()) JSCallbackObject<JSGlobalObject>(globalObjectClass);
+ JSGlobalObject* globalObject = JSCallbackObject<JSGlobalObject>::create(*vm, globalObjectClass, JSCallbackObject<JSGlobalObject>::createStructure(*vm, 0, jsNull()));
ExecState* exec = globalObject->globalExec();
JSValue prototype = globalObjectClass->prototype(exec);
if (!prototype)
prototype = jsNull();
- globalObject->resetPrototype(prototype);
+ globalObject->resetPrototype(*vm, prototype);
return JSGlobalContextRetain(toGlobalRef(exec));
}
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
- JSGlobalData& globalData = exec->globalData();
+ VM& vm = exec->vm();
gcProtect(exec->dynamicGlobalObject());
- globalData.ref();
+ vm.ref();
return ctx;
}
void JSGlobalContextRelease(JSGlobalContextRef ctx)
{
+ IdentifierTable* savedIdentifierTable;
ExecState* exec = toJS(ctx);
- JSLock lock(exec);
-
- JSGlobalData& globalData = exec->globalData();
- IdentifierTable* savedIdentifierTable = setCurrentIdentifierTable(globalData.identifierTable);
+ {
+ JSLockHolder lock(exec);
- gcUnprotect(exec->dynamicGlobalObject());
+ VM& vm = exec->vm();
+ savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
- if (globalData.refCount() == 2) { // One reference is held by JSGlobalObject, another added by JSGlobalContextRetain().
- // The last reference was released, this is our last chance to collect.
- globalData.heap.destroy();
- } else
- globalData.heap.collectAllGarbage();
-
- globalData.deref();
+ bool protectCountIsZero = Heap::heap(exec->dynamicGlobalObject())->unprotect(exec->dynamicGlobalObject());
+ if (protectCountIsZero)
+ vm.heap.reportAbandonedObjectGraph();
+ vm.deref();
+ }
- setCurrentIdentifierTable(savedIdentifierTable);
+ wtfThreadData().setCurrentIdentifierTable(savedIdentifierTable);
}
JSObjectRef JSContextGetGlobalObject(JSContextRef ctx)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
// It is necessary to call toThisObject to get the wrapper object when used with WebCore.
- return toRef(exec->lexicalGlobalObject()->toThisObject(exec));
+ return toRef(exec->lexicalGlobalObject()->methodTable()->toThisObject(exec->lexicalGlobalObject(), exec));
}
JSContextGroupRef JSContextGetGroup(JSContextRef ctx)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
- return toRef(&exec->globalData());
+ return toRef(&exec->vm());
}
JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
return toGlobalRef(exec->lexicalGlobalObject()->globalExec());
}
+
+JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize)
+{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+ ExecState* exec = toJS(ctx);
+ JSLockHolder lock(exec);
+ StringBuilder builder;
+ Vector<StackFrame> stackTrace;
+ Interpreter::getStackTrace(&exec->vm(), stackTrace, maxStackSize);
+
+ for (size_t i = 0; i < stackTrace.size(); i++) {
+ String urlString;
+ String functionName;
+ StackFrame& frame = stackTrace[i];
+ JSValue function = frame.callee.get();
+ if (frame.callee)
+ functionName = frame.friendlyFunctionName(exec);
+ else {
+ // Caller is unknown, but if frame is empty we should still add the frame, because
+ // something called us, and gave us arguments.
+ if (i)
+ break;
+ }
+ unsigned lineNumber;
+ unsigned column;
+ frame.computeLineAndColumn(lineNumber, column);
+ if (!builder.isEmpty())
+ builder.append('\n');
+ builder.append('#');
+ builder.appendNumber(i);
+ builder.append(' ');
+ builder.append(functionName);
+ builder.appendLiteral("() at ");
+ builder.append(urlString);
+ if (frame.codeType != StackFrameNativeCode) {
+ builder.append(':');
+ builder.appendNumber(lineNumber);
+ }
+ if (!function)
+ break;
+ }
+ return OpaqueJSString::create(builder.toString()).leakRef();
+}
+
+