#include "JSClassRef.h"
#include "JSGlobalObject.h"
#include "JSObject.h"
-#include "UStringBuilder.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::createContextGroup(ThreadStackTypeSmall).leakRef());
+ 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.
-#ifndef 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::createContextGroup(ThreadStackTypeSmall);
-
- APIEntryShim entryShim(globalData.get(), false);
+ RefPtr<VM> vm = group ? PassRefPtr<VM>(toJS(group)) : VM::createContextGroup();
-#if ENABLE(JSC_MULTIPLE_THREADS)
- globalData->makeUsableFromMultipleThreads();
-#endif
+ APIEntryShim entryShim(vm.get(), false);
+ vm->makeUsableFromMultipleThreads();
if (!globalObjectClass) {
- JSGlobalObject* globalObject = new (globalData.get()) JSGlobalObject(*globalData, JSGlobalObject::createStructure(*globalData, jsNull()));
+ JSGlobalObject* globalObject = JSGlobalObject::create(*vm, JSGlobalObject::createStructure(*vm, jsNull()));
return JSGlobalContextRetain(toGlobalRef(globalObject->globalExec()));
}
- JSGlobalObject* globalObject = new (globalData.get()) JSCallbackObject<JSGlobalObject>(*globalData, globalObjectClass, JSCallbackObject<JSGlobalObject>::createStructure(*globalData, jsNull()));
+ 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(*globalData, 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();
- JSGlobalObject* dgo = exec->dynamicGlobalObject();
- IdentifierTable* savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(globalData.identifierTable);
-
- // One reference is held by JSGlobalObject, another added by JSGlobalContextRetain().
- bool releasingContextGroup = globalData.refCount() == 2;
- bool releasingGlobalObject = Heap::heap(dgo)->unprotect(dgo);
- // If this is the last reference to a global data, it should also
- // be the only remaining reference to the global object too!
- ASSERT(!releasingContextGroup || releasingGlobalObject);
-
- // An API 'JSGlobalContextRef' retains two things - a global object and a
- // global data (or context group, in API terminology).
- // * If this is the last reference to any contexts in the given context group,
- // call destroy on the heap (the global data is being freed).
- // * If this was the last reference to the global object, then unprotecting
- // it may release a lot of GC memory - tickle the activity callback to
- // garbage collect soon.
- // * If there are more references remaining the the global object, then do nothing
- // (specifically that is more protects, which we assume come from other JSGlobalContextRefs).
- if (releasingContextGroup) {
- globalData.clearBuiltinStructures();
- globalData.heap.destroy();
- } else if (releasingGlobalObject) {
- globalData.heap.activityCallback()->synchronize();
- (*globalData.heap.activityCallback())();
- }
+ {
+ JSLockHolder lock(exec);
+
+ VM& vm = exec->vm();
+ savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
- globalData.deref();
+ bool protectCountIsZero = Heap::heap(exec->dynamicGlobalObject())->unprotect(exec->dynamicGlobalObject());
+ if (protectCountIsZero)
+ vm.heap.reportAbandonedObjectGraph();
+ vm.deref();
+ }
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);
JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize)
{
- ExecState* exec = toJS(ctx);
- JSLock lock(exec);
-
- unsigned count = 0;
- UStringBuilder builder;
- CallFrame* callFrame = exec;
- UString functionName;
- if (exec->callee()) {
- if (asObject(exec->callee())->inherits(&InternalFunction::s_info)) {
- functionName = asInternalFunction(exec->callee())->name(exec);
- builder.append("#0 ");
- builder.append(functionName);
- builder.append("() ");
- count++;
- }
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
}
- while (true) {
- ASSERT(callFrame);
- int signedLineNumber;
- intptr_t sourceID;
- UString urlString;
- JSValue function;
-
- UString levelStr = UString::number(count);
-
- exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function);
-
- if (function)
- functionName = asFunction(function)->name(exec);
+ 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 (count)
+ if (i)
break;
}
- unsigned lineNumber = signedLineNumber >= 0 ? signedLineNumber : 0;
+ unsigned lineNumber;
+ unsigned column;
+ frame.computeLineAndColumn(lineNumber, column);
if (!builder.isEmpty())
- builder.append("\n");
- builder.append("#");
- builder.append(levelStr);
- builder.append(" ");
+ builder.append('\n');
+ builder.append('#');
+ builder.appendNumber(i);
+ builder.append(' ');
builder.append(functionName);
- builder.append("() at ");
+ builder.appendLiteral("() at ");
builder.append(urlString);
- builder.append(":");
- builder.append(UString::number(lineNumber));
- if (!function || ++count == maxStackSize)
+ if (frame.codeType != StackFrameNativeCode) {
+ builder.append(':');
+ builder.appendNumber(lineNumber);
+ }
+ if (!function)
break;
- callFrame = callFrame->callerFrame();
}
- return OpaqueJSString::create(builder.toUString()).leakRef();
+ return OpaqueJSString::create(builder.toString()).leakRef();
}