#include "config.h"
#include "JSContextRef.h"
#include "JSContextRefPrivate.h"
#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 "Operations.h"
#include "SourceProvider.h"
#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringHash.h>
#if OS(DARWIN)
#include <mach-o/dyld.h>
static const int32_t webkitFirstVersionWithConcurrentGlobalContexts = 0x2100500; #endif
using namespace JSC;
JSContextGroupRef JSContextGroupCreate()
{
initializeThreading();
return toRef(VM::createContextGroup().leakRef());
}
JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group)
{
toJS(group)->ref();
return group;
}
void JSContextGroupRelease(JSContextGroupRef group)
{
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());
}
JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
{
initializeThreading();
#if OS(DARWIN)
if (NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitFirstVersionWithConcurrentGlobalContexts) {
return JSGlobalContextCreateInGroup(toRef(&VM::sharedInstance()), globalObjectClass);
}
#endif // OS(DARWIN)
return JSGlobalContextCreateInGroup(0, globalObjectClass);
}
JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClassRef globalObjectClass)
{
initializeThreading();
RefPtr<VM> vm = group ? PassRefPtr<VM>(toJS(group)) : VM::createContextGroup();
APIEntryShim entryShim(vm.get(), false);
vm->makeUsableFromMultipleThreads();
if (!globalObjectClass) {
JSGlobalObject* globalObject = JSGlobalObject::create(*vm, JSGlobalObject::createStructure(*vm, jsNull()));
return JSGlobalContextRetain(toGlobalRef(globalObject->globalExec()));
}
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(*vm, prototype);
return JSGlobalContextRetain(toGlobalRef(exec));
}
JSGlobalContextRef JSGlobalContextRetain(JSGlobalContextRef ctx)
{
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
VM& vm = exec->vm();
gcProtect(exec->dynamicGlobalObject());
vm.ref();
return ctx;
}
void JSGlobalContextRelease(JSGlobalContextRef ctx)
{
IdentifierTable* savedIdentifierTable;
ExecState* exec = toJS(ctx);
{
JSLockHolder lock(exec);
VM& vm = exec->vm();
savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
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);
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->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 {
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();
}