summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/API
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/API')
-rw-r--r--Source/JavaScriptCore/API/APICast.h46
-rw-r--r--Source/JavaScriptCore/API/APIShims.h58
-rw-r--r--Source/JavaScriptCore/API/JSAPIWrapperObject.h60
-rw-r--r--Source/JavaScriptCore/API/JSAPIWrapperObject.mm112
-rw-r--r--Source/JavaScriptCore/API/JSBase.cpp41
-rw-r--r--Source/JavaScriptCore/API/JSBase.h9
-rw-r--r--Source/JavaScriptCore/API/JSCTestRunnerUtils.cpp72
-rw-r--r--Source/JavaScriptCore/API/JSCTestRunnerUtils.h39
-rw-r--r--Source/JavaScriptCore/API/JSCallbackConstructor.cpp22
-rw-r--r--Source/JavaScriptCore/API/JSCallbackConstructor.h4
-rw-r--r--Source/JavaScriptCore/API/JSCallbackFunction.cpp32
-rw-r--r--Source/JavaScriptCore/API/JSCallbackFunction.h18
-rw-r--r--Source/JavaScriptCore/API/JSCallbackObject.cpp23
-rw-r--r--Source/JavaScriptCore/API/JSCallbackObject.h26
-rw-r--r--Source/JavaScriptCore/API/JSCallbackObjectFunctions.h103
-rw-r--r--Source/JavaScriptCore/API/JSClassRef.cpp17
-rw-r--r--Source/JavaScriptCore/API/JSClassRef.h11
-rw-r--r--Source/JavaScriptCore/API/JSContext.h129
-rw-r--r--Source/JavaScriptCore/API/JSContext.mm289
-rw-r--r--Source/JavaScriptCore/API/JSContextInternal.h80
-rw-r--r--Source/JavaScriptCore/API/JSContextRef.cpp138
-rw-r--r--Source/JavaScriptCore/API/JSContextRefPrivate.h48
-rw-r--r--Source/JavaScriptCore/API/JSExport.h130
-rw-r--r--Source/JavaScriptCore/API/JSManagedValue.h66
-rw-r--r--Source/JavaScriptCore/API/JSManagedValue.mm117
-rw-r--r--Source/JavaScriptCore/API/JSObjectRef.cpp157
-rw-r--r--Source/JavaScriptCore/API/JSProfilerPrivate.cpp6
-rw-r--r--Source/JavaScriptCore/API/JSScriptRef.cpp151
-rw-r--r--Source/JavaScriptCore/API/JSScriptRefPrivate.h99
-rw-r--r--Source/JavaScriptCore/API/JSStringRef.cpp8
-rw-r--r--Source/JavaScriptCore/API/JSStringRefCF.cpp6
-rw-r--r--Source/JavaScriptCore/API/JSStringRefPrivate.h41
-rw-r--r--Source/JavaScriptCore/API/JSStringRefQt.cpp50
-rw-r--r--Source/JavaScriptCore/API/JSStringRefQt.h45
-rw-r--r--Source/JavaScriptCore/API/JSValue.h306
-rw-r--r--Source/JavaScriptCore/API/JSValue.mm1131
-rw-r--r--Source/JavaScriptCore/API/JSValueInternal.h58
-rw-r--r--Source/JavaScriptCore/API/JSValueRef.cpp121
-rw-r--r--Source/JavaScriptCore/API/JSVirtualMachine.h58
-rw-r--r--Source/JavaScriptCore/API/JSVirtualMachine.mm255
-rw-r--r--Source/JavaScriptCore/API/JSVirtualMachineInternal.h57
-rw-r--r--Source/JavaScriptCore/API/JSWeakObjectMapRefPrivate.cpp18
-rw-r--r--Source/JavaScriptCore/API/JSWrapperMap.h47
-rw-r--r--Source/JavaScriptCore/API/JSWrapperMap.mm521
-rw-r--r--Source/JavaScriptCore/API/JavaScriptCore.h10
-rw-r--r--Source/JavaScriptCore/API/ObjCCallbackFunction.h73
-rw-r--r--Source/JavaScriptCore/API/ObjCCallbackFunction.mm615
-rw-r--r--Source/JavaScriptCore/API/ObjcRuntimeExtras.h231
-rw-r--r--Source/JavaScriptCore/API/OpaqueJSString.cpp6
-rw-r--r--Source/JavaScriptCore/API/OpaqueJSString.h13
-rw-r--r--Source/JavaScriptCore/API/tests/JSNode.c1
-rw-r--r--Source/JavaScriptCore/API/tests/JSNodeList.c1
-rw-r--r--Source/JavaScriptCore/API/tests/minidom.c1
-rw-r--r--Source/JavaScriptCore/API/tests/testapi.c315
-rw-r--r--Source/JavaScriptCore/API/tests/testapi.js4
-rw-r--r--Source/JavaScriptCore/API/tests/testapi.mm841
56 files changed, 6675 insertions, 261 deletions
diff --git a/Source/JavaScriptCore/API/APICast.h b/Source/JavaScriptCore/API/APICast.h
index f019a7a4b..fc5d71b2e 100644
--- a/Source/JavaScriptCore/API/APICast.h
+++ b/Source/JavaScriptCore/API/APICast.h
@@ -27,14 +27,13 @@
#define APICast_h
#include "JSAPIValueWrapper.h"
+#include "JSCJSValue.h"
#include "JSGlobalObject.h"
-#include "JSValue.h"
-#include <wtf/UnusedParam.h>
namespace JSC {
class ExecState;
class PropertyNameArray;
- class JSGlobalData;
+ class VM;
class JSObject;
class JSValue;
}
@@ -63,46 +62,63 @@ inline JSC::ExecState* toJS(JSGlobalContextRef c)
inline JSC::JSValue toJS(JSC::ExecState* exec, JSValueRef v)
{
ASSERT_UNUSED(exec, exec);
- ASSERT(v);
#if USE(JSVALUE32_64)
JSC::JSCell* jsCell = reinterpret_cast<JSC::JSCell*>(const_cast<OpaqueJSValue*>(v));
if (!jsCell)
- return JSC::JSValue();
+ return JSC::jsNull();
+ JSC::JSValue result;
if (jsCell->isAPIValueWrapper())
- return JSC::jsCast<JSC::JSAPIValueWrapper*>(jsCell)->value();
- return jsCell;
+ result = JSC::jsCast<JSC::JSAPIValueWrapper*>(jsCell)->value();
+ else
+ result = jsCell;
#else
- return JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(const_cast<OpaqueJSValue*>(v)));
+ JSC::JSValue result = JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(const_cast<OpaqueJSValue*>(v)));
#endif
+ if (!result)
+ return JSC::jsNull();
+ if (result.isCell())
+ RELEASE_ASSERT(result.asCell()->methodTable());
+ return result;
}
inline JSC::JSValue toJSForGC(JSC::ExecState* exec, JSValueRef v)
{
ASSERT_UNUSED(exec, exec);
- ASSERT(v);
#if USE(JSVALUE32_64)
JSC::JSCell* jsCell = reinterpret_cast<JSC::JSCell*>(const_cast<OpaqueJSValue*>(v));
if (!jsCell)
return JSC::JSValue();
- return jsCell;
+ JSC::JSValue result = jsCell;
#else
- return JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(const_cast<OpaqueJSValue*>(v)));
+ JSC::JSValue result = JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(const_cast<OpaqueJSValue*>(v)));
#endif
+ if (result && result.isCell())
+ RELEASE_ASSERT(result.asCell()->methodTable());
+ return result;
}
-inline JSC::JSObject* toJS(JSObjectRef o)
+// Used in JSObjectGetPrivate as that may be called during finalization
+inline JSC::JSObject* uncheckedToJS(JSObjectRef o)
{
return reinterpret_cast<JSC::JSObject*>(o);
}
+inline JSC::JSObject* toJS(JSObjectRef o)
+{
+ JSC::JSObject* object = uncheckedToJS(o);
+ if (object)
+ RELEASE_ASSERT(object->methodTable());
+ return object;
+}
+
inline JSC::PropertyNameArray* toJS(JSPropertyNameAccumulatorRef a)
{
return reinterpret_cast<JSC::PropertyNameArray*>(a);
}
-inline JSC::JSGlobalData* toJS(JSContextGroupRef g)
+inline JSC::VM* toJS(JSContextGroupRef g)
{
- return reinterpret_cast<JSC::JSGlobalData*>(const_cast<OpaqueJSContextGroup*>(g));
+ return reinterpret_cast<JSC::VM*>(const_cast<OpaqueJSContextGroup*>(g));
}
inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v)
@@ -145,7 +161,7 @@ inline JSPropertyNameAccumulatorRef toRef(JSC::PropertyNameArray* l)
return reinterpret_cast<JSPropertyNameAccumulatorRef>(l);
}
-inline JSContextGroupRef toRef(JSC::JSGlobalData* g)
+inline JSContextGroupRef toRef(JSC::VM* g)
{
return reinterpret_cast<JSContextGroupRef>(g);
}
diff --git a/Source/JavaScriptCore/API/APIShims.h b/Source/JavaScriptCore/API/APIShims.h
index ef5f10466..d8e1fb790 100644
--- a/Source/JavaScriptCore/API/APIShims.h
+++ b/Source/JavaScriptCore/API/APIShims.h
@@ -35,34 +35,22 @@
namespace JSC {
class APIEntryShimWithoutLock {
-public:
- enum RefGlobalDataTag { DontRefGlobalData = 0, RefGlobalData };
-
protected:
- APIEntryShimWithoutLock(JSGlobalData* globalData, bool registerThread, RefGlobalDataTag shouldRefGlobalData)
- : m_shouldRefGlobalData(shouldRefGlobalData)
- , m_globalData(globalData)
- , m_entryIdentifierTable(wtfThreadData().setCurrentIdentifierTable(globalData->identifierTable))
+ APIEntryShimWithoutLock(VM* vm, bool registerThread)
+ : m_vm(vm)
+ , m_entryIdentifierTable(wtfThreadData().setCurrentIdentifierTable(vm->identifierTable))
{
- if (shouldRefGlobalData)
- m_globalData->ref();
- UNUSED_PARAM(registerThread);
if (registerThread)
- globalData->heap.machineThreads().addCurrentThread();
- m_globalData->heap.activityCallback()->synchronize();
- m_globalData->heap.sweeper()->synchronize();
+ vm->heap.machineThreads().addCurrentThread();
}
~APIEntryShimWithoutLock()
{
wtfThreadData().setCurrentIdentifierTable(m_entryIdentifierTable);
- if (m_shouldRefGlobalData)
- m_globalData->deref();
}
protected:
- RefGlobalDataTag m_shouldRefGlobalData;
- JSGlobalData* m_globalData;
+ RefPtr<VM> m_vm;
IdentifierTable* m_entryIdentifierTable;
};
@@ -70,57 +58,45 @@ class APIEntryShim : public APIEntryShimWithoutLock {
public:
// Normal API entry
APIEntryShim(ExecState* exec, bool registerThread = true)
- : APIEntryShimWithoutLock(&exec->globalData(), registerThread, RefGlobalData)
+ : APIEntryShimWithoutLock(&exec->vm(), registerThread)
+ , m_lockHolder(exec)
{
- init();
}
- // This constructor is necessary for HeapTimer to prevent it from accidentally resurrecting
- // the ref count of a "dead" JSGlobalData.
- APIEntryShim(JSGlobalData* globalData, RefGlobalDataTag refGlobalData, bool registerThread = true)
- : APIEntryShimWithoutLock(globalData, registerThread, refGlobalData)
+ // JSPropertyNameAccumulator only has a vm.
+ APIEntryShim(VM* vm, bool registerThread = true)
+ : APIEntryShimWithoutLock(vm, registerThread)
+ , m_lockHolder(vm)
{
- init();
- }
-
- // JSPropertyNameAccumulator only has a globalData.
- APIEntryShim(JSGlobalData* globalData, bool registerThread = true)
- : APIEntryShimWithoutLock(globalData, registerThread, RefGlobalData)
- {
- init();
}
~APIEntryShim()
{
- m_globalData->timeoutChecker.stop();
- m_globalData->apiLock().unlock();
+ // Destroying our JSLockHolder should also destroy the VM.
+ m_vm.clear();
}
private:
- void init()
- {
- m_globalData->apiLock().lock();
- m_globalData->timeoutChecker.start();
- }
+ JSLockHolder m_lockHolder;
};
class APICallbackShim {
public:
APICallbackShim(ExecState* exec)
: m_dropAllLocks(exec)
- , m_globalData(&exec->globalData())
+ , m_vm(&exec->vm())
{
wtfThreadData().resetCurrentIdentifierTable();
}
~APICallbackShim()
{
- wtfThreadData().setCurrentIdentifierTable(m_globalData->identifierTable);
+ wtfThreadData().setCurrentIdentifierTable(m_vm->identifierTable);
}
private:
JSLock::DropAllLocks m_dropAllLocks;
- JSGlobalData* m_globalData;
+ VM* m_vm;
};
}
diff --git a/Source/JavaScriptCore/API/JSAPIWrapperObject.h b/Source/JavaScriptCore/API/JSAPIWrapperObject.h
new file mode 100644
index 000000000..909039771
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSAPIWrapperObject.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSAPIWrapperObject_h
+#define JSAPIWrapperObject_h
+
+#include "JSBase.h"
+#include "JSDestructibleObject.h"
+#include "WeakReferenceHarvester.h"
+
+#if JSC_OBJC_API_ENABLED
+
+namespace JSC {
+
+class JSAPIWrapperObject : public JSDestructibleObject {
+public:
+ typedef JSDestructibleObject Base;
+
+ void finishCreation(VM&);
+ static void visitChildren(JSCell*, JSC::SlotVisitor&);
+
+ void* wrappedObject() { return m_wrappedObject; }
+ void setWrappedObject(void*);
+
+protected:
+ static const unsigned StructureFlags = OverridesVisitChildren | Base::StructureFlags;
+
+ JSAPIWrapperObject(VM&, Structure*);
+
+private:
+ void* m_wrappedObject;
+};
+
+} // namespace JSC
+
+#endif // JSC_OBJC_API_ENABLED
+
+#endif // JSAPIWrapperObject_h
diff --git a/Source/JavaScriptCore/API/JSAPIWrapperObject.mm b/Source/JavaScriptCore/API/JSAPIWrapperObject.mm
new file mode 100644
index 000000000..c06de3978
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSAPIWrapperObject.mm
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSAPIWrapperObject.h"
+
+#include "JSCJSValueInlines.h"
+#include "JSCallbackObject.h"
+#include "JSCellInlines.h"
+#include "JSVirtualMachineInternal.h"
+#include "SlotVisitorInlines.h"
+#include "Structure.h"
+#include "StructureInlines.h"
+
+#if JSC_OBJC_API_ENABLED
+
+class JSAPIWrapperObjectHandleOwner : public JSC::WeakHandleOwner {
+public:
+ virtual void finalize(JSC::Handle<JSC::Unknown>, void*);
+ virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&);
+};
+
+static JSAPIWrapperObjectHandleOwner* jsAPIWrapperObjectHandleOwner()
+{
+ DEFINE_STATIC_LOCAL(JSAPIWrapperObjectHandleOwner, jsWrapperObjectHandleOwner, ());
+ return &jsWrapperObjectHandleOwner;
+}
+
+void JSAPIWrapperObjectHandleOwner::finalize(JSC::Handle<JSC::Unknown> handle, void*)
+{
+ JSC::JSAPIWrapperObject* wrapperObject = JSC::jsCast<JSC::JSAPIWrapperObject*>(handle.get().asCell());
+ if (!wrapperObject->wrappedObject())
+ return;
+ [static_cast<id>(wrapperObject->wrappedObject()) release];
+ JSC::WeakSet::deallocate(JSC::WeakImpl::asWeakImpl(handle.slot()));
+}
+
+bool JSAPIWrapperObjectHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, JSC::SlotVisitor& visitor)
+{
+ JSC::JSAPIWrapperObject* wrapperObject = JSC::jsCast<JSC::JSAPIWrapperObject*>(handle.get().asCell());
+ // We use the JSGlobalObject when processing weak handles to prevent the situation where using
+ // the same Objective-C object in multiple global objects keeps all of the global objects alive.
+ if (!wrapperObject->wrappedObject())
+ return false;
+ return JSC::Heap::isMarked(wrapperObject->structure()->globalObject()) && visitor.containsOpaqueRoot(wrapperObject->wrappedObject());
+}
+
+namespace JSC {
+
+template <> const ClassInfo JSCallbackObject<JSAPIWrapperObject>::s_info = { "JSAPIWrapperObject", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSCallbackObject) };
+
+template<> const bool JSCallbackObject<JSAPIWrapperObject>::needsDestruction = true;
+
+template <>
+Structure* JSCallbackObject<JSAPIWrapperObject>::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
+{
+ return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
+}
+
+JSAPIWrapperObject::JSAPIWrapperObject(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ , m_wrappedObject(0)
+{
+}
+
+void JSAPIWrapperObject::finishCreation(VM& vm)
+{
+ Base::finishCreation(vm);
+ WeakSet::allocate(this, jsAPIWrapperObjectHandleOwner(), 0); // Balanced in JSAPIWrapperObjectHandleOwner::finalize.
+}
+
+void JSAPIWrapperObject::setWrappedObject(void* wrappedObject)
+{
+ ASSERT(!m_wrappedObject);
+ m_wrappedObject = [static_cast<id>(wrappedObject) retain];
+}
+
+void JSAPIWrapperObject::visitChildren(JSCell* cell, JSC::SlotVisitor& visitor)
+{
+ JSAPIWrapperObject* thisObject = JSC::jsCast<JSAPIWrapperObject*>(cell);
+ COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
+ Base::visitChildren(cell, visitor);
+
+ if (thisObject->wrappedObject())
+ scanExternalObjectGraph(cell->structure()->globalObject()->vm(), visitor, thisObject->wrappedObject());
+}
+
+} // namespace JSC
+
+#endif // JSC_OBJC_API_ENABLED
diff --git a/Source/JavaScriptCore/API/JSBase.cpp b/Source/JavaScriptCore/API/JSBase.cpp
index 677c68187..7669ff1a9 100644
--- a/Source/JavaScriptCore/API/JSBase.cpp
+++ b/Source/JavaScriptCore/API/JSBase.cpp
@@ -29,20 +29,25 @@
#include "APICast.h"
#include "APIShims.h"
+#include "CallFrame.h"
+#include "Completion.h"
+#include "InitializeThreading.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "JSObject.h"
#include "OpaqueJSString.h"
+#include "Operations.h"
#include "SourceCode.h"
-#include <interpreter/CallFrame.h>
-#include <runtime/InitializeThreading.h>
-#include <runtime/Completion.h>
-#include <runtime/JSGlobalObject.h>
-#include <runtime/JSLock.h>
-#include <runtime/JSObject.h>
#include <wtf/text/StringHash.h>
using namespace JSC;
JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef thisObject, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -70,6 +75,10 @@ JSValueRef JSEvaluateScript(JSContextRef ctx, JSStringRef script, JSObjectRef th
bool JSCheckScriptSyntax(JSContextRef ctx, JSStringRef script, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -100,12 +109,28 @@ void JSGarbageCollect(JSContextRef ctx)
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec, false);
- exec->globalData().heap.reportAbandonedObjectGraph();
+ exec->vm().heap.reportAbandonedObjectGraph();
}
void JSReportExtraMemoryCost(JSContextRef ctx, size_t size)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ ExecState* exec = toJS(ctx);
+ APIEntryShim entryShim(exec);
+ exec->vm().heap.reportExtraMemoryCost(size);
+}
+
+extern "C" JS_EXPORT void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+
+void JSSynchronousGarbageCollectForDebugging(JSContextRef ctx)
+{
+ if (!ctx)
+ return;
+
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
- exec->globalData().heap.reportExtraMemoryCost(size);
+ exec->vm().heap.collectAllGarbage();
}
diff --git a/Source/JavaScriptCore/API/JSBase.h b/Source/JavaScriptCore/API/JSBase.h
index f46a41755..50e8f1e64 100644
--- a/Source/JavaScriptCore/API/JSBase.h
+++ b/Source/JavaScriptCore/API/JSBase.h
@@ -30,6 +30,10 @@
#include <stdbool.h>
#endif
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
+
/* JavaScript engine interface */
/*! @typedef JSContextGroupRef A group that associates JavaScript contexts with one another. Contexts in the same group may share and exchange JavaScript objects. */
@@ -135,4 +139,9 @@ JS_EXPORT void JSGarbageCollect(JSContextRef ctx);
}
#endif
+/* Enable the Objective-C API for platforms with a modern runtime. */
+#if !defined(JSC_OBJC_API_ENABLED)
+#define JSC_OBJC_API_ENABLED (defined(__clang__) && defined(__APPLE__) && defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 && !defined(__i386__))
+#endif
+
#endif /* JSBase_h */
diff --git a/Source/JavaScriptCore/API/JSCTestRunnerUtils.cpp b/Source/JavaScriptCore/API/JSCTestRunnerUtils.cpp
new file mode 100644
index 000000000..394246711
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSCTestRunnerUtils.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSCTestRunnerUtils.h"
+
+#include "APICast.h"
+#include "CodeBlock.h"
+#include "Operations.h"
+
+namespace JSC {
+
+static FunctionExecutable* getExecutable(JSContextRef context, JSValueRef theFunctionValueRef)
+{
+ ExecState* exec = toJS(context);
+ JSValue theFunctionValue = toJS(exec, theFunctionValueRef);
+
+ JSFunction* theFunction = jsDynamicCast<JSFunction*>(theFunctionValue);
+ if (!theFunction)
+ return 0;
+
+ FunctionExecutable* executable = jsDynamicCast<FunctionExecutable*>(
+ theFunction->executable());
+ return executable;
+}
+
+JSValueRef numberOfDFGCompiles(JSContextRef context, JSValueRef theFunctionValueRef)
+{
+ if (FunctionExecutable* executable = getExecutable(context, theFunctionValueRef)) {
+ CodeBlock* baselineCodeBlock = executable->baselineCodeBlockFor(CodeForCall);
+
+ if (!baselineCodeBlock)
+ return JSValueMakeNumber(context, 0);
+
+ return JSValueMakeNumber(context, baselineCodeBlock->numberOfDFGCompiles());
+ }
+
+ return JSValueMakeUndefined(context);
+}
+
+JSValueRef setNeverInline(JSContextRef context, JSValueRef theFunctionValueRef)
+{
+ if (FunctionExecutable* executable = getExecutable(context, theFunctionValueRef))
+ executable->setNeverInline(true);
+
+ return JSValueMakeUndefined(context);
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/API/JSCTestRunnerUtils.h b/Source/JavaScriptCore/API/JSCTestRunnerUtils.h
new file mode 100644
index 000000000..aaecdd5c9
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSCTestRunnerUtils.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSCTestRunnerUtils_h
+#define JSCTestRunnerUtils_h
+
+#include <JavaScriptCore/JSContextRef.h>
+#include <JavaScriptCore/JSValueRef.h>
+
+namespace JSC {
+
+JS_EXPORT_PRIVATE JSValueRef numberOfDFGCompiles(JSContextRef, JSValueRef theFunction);
+JS_EXPORT_PRIVATE JSValueRef setNeverInline(JSContextRef, JSValueRef theFunction);
+
+} // namespace JSC
+
+#endif // JSCTestRunnerUtils_h
diff --git a/Source/JavaScriptCore/API/JSCallbackConstructor.cpp b/Source/JavaScriptCore/API/JSCallbackConstructor.cpp
index c43182e8f..8340c10b4 100644
--- a/Source/JavaScriptCore/API/JSCallbackConstructor.cpp
+++ b/Source/JavaScriptCore/API/JSCallbackConstructor.cpp
@@ -28,10 +28,11 @@
#include "APIShims.h"
#include "APICast.h"
-#include <runtime/Error.h>
-#include <runtime/JSGlobalObject.h>
-#include <runtime/JSLock.h>
-#include <runtime/ObjectPrototype.h>
+#include "Error.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "ObjectPrototype.h"
+#include "Operations.h"
#include <wtf/Vector.h>
namespace JSC {
@@ -39,7 +40,7 @@ namespace JSC {
const ClassInfo JSCallbackConstructor::s_info = { "CallbackConstructor", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSCallbackConstructor) };
JSCallbackConstructor::JSCallbackConstructor(JSGlobalObject* globalObject, Structure* structure, JSClassRef jsClass, JSObjectCallAsConstructorCallback callback)
- : JSDestructibleObject(globalObject->globalData(), structure)
+ : JSDestructibleObject(globalObject->vm(), structure)
, m_class(jsClass)
, m_callback(callback)
{
@@ -47,7 +48,7 @@ JSCallbackConstructor::JSCallbackConstructor(JSGlobalObject* globalObject, Struc
void JSCallbackConstructor::finishCreation(JSGlobalObject* globalObject, JSClassRef jsClass)
{
- Base::finishCreation(globalObject->globalData());
+ Base::finishCreation(globalObject->vm());
ASSERT(inherits(&s_info));
if (m_class)
JSClassRetain(jsClass);
@@ -72,10 +73,11 @@ static EncodedJSValue JSC_HOST_CALL constructJSCallback(ExecState* exec)
JSObjectCallAsConstructorCallback callback = jsCast<JSCallbackConstructor*>(constructor)->callback();
if (callback) {
- int argumentCount = static_cast<int>(exec->argumentCount());
- Vector<JSValueRef, 16> arguments(argumentCount);
- for (int i = 0; i < argumentCount; i++)
- arguments[i] = toRef(exec, exec->argument(i));
+ size_t argumentCount = exec->argumentCount();
+ Vector<JSValueRef, 16> arguments;
+ arguments.reserveInitialCapacity(argumentCount);
+ for (size_t i = 0; i < argumentCount; ++i)
+ arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
JSValueRef exception = 0;
JSObjectRef result;
diff --git a/Source/JavaScriptCore/API/JSCallbackConstructor.h b/Source/JavaScriptCore/API/JSCallbackConstructor.h
index 2bb4d07af..72100e672 100644
--- a/Source/JavaScriptCore/API/JSCallbackConstructor.h
+++ b/Source/JavaScriptCore/API/JSCallbackConstructor.h
@@ -48,9 +48,9 @@ public:
JSObjectCallAsConstructorCallback callback() const { return m_callback; }
static const ClassInfo s_info;
- static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
{
- return Structure::create(globalData, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
+ return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
}
protected:
diff --git a/Source/JavaScriptCore/API/JSCallbackFunction.cpp b/Source/JavaScriptCore/API/JSCallbackFunction.cpp
index aada87bfa..c29b9077c 100644
--- a/Source/JavaScriptCore/API/JSCallbackFunction.cpp
+++ b/Source/JavaScriptCore/API/JSCallbackFunction.cpp
@@ -29,11 +29,13 @@
#include "APIShims.h"
#include "APICast.h"
#include "CodeBlock.h"
+#include "Error.h"
#include "ExceptionHelpers.h"
-#include "JSFunction.h"
#include "FunctionPrototype.h"
-#include <runtime/JSGlobalObject.h>
-#include <runtime/JSLock.h>
+#include "JSFunction.h"
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "Operations.h"
#include <wtf/Vector.h>
namespace JSC {
@@ -42,28 +44,36 @@ ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSCallbackFunction);
const ClassInfo JSCallbackFunction::s_info = { "CallbackFunction", &InternalFunction::s_info, 0, 0, CREATE_METHOD_TABLE(JSCallbackFunction) };
-JSCallbackFunction::JSCallbackFunction(JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback callback)
- : InternalFunction(globalObject, globalObject->callbackFunctionStructure())
+JSCallbackFunction::JSCallbackFunction(JSGlobalObject* globalObject, Structure* structure, JSObjectCallAsFunctionCallback callback)
+ : InternalFunction(globalObject, structure)
, m_callback(callback)
{
}
-void JSCallbackFunction::finishCreation(JSGlobalData& globalData, const String& name)
+void JSCallbackFunction::finishCreation(VM& vm, const String& name)
{
- Base::finishCreation(globalData, name);
+ Base::finishCreation(vm, name);
ASSERT(inherits(&s_info));
}
+JSCallbackFunction* JSCallbackFunction::create(ExecState* exec, JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback callback, const String& name)
+{
+ JSCallbackFunction* function = new (NotNull, allocateCell<JSCallbackFunction>(*exec->heap())) JSCallbackFunction(globalObject, globalObject->callbackFunctionStructure(), callback);
+ function->finishCreation(exec->vm(), name);
+ return function;
+}
+
EncodedJSValue JSCallbackFunction::call(ExecState* exec)
{
JSContextRef execRef = toRef(exec);
JSObjectRef functionRef = toRef(exec->callee());
JSObjectRef thisObjRef = toRef(exec->hostThisValue().toThisObject(exec));
- int argumentCount = static_cast<int>(exec->argumentCount());
- Vector<JSValueRef, 16> arguments(argumentCount);
- for (int i = 0; i < argumentCount; i++)
- arguments[i] = toRef(exec, exec->argument(i));
+ size_t argumentCount = exec->argumentCount();
+ Vector<JSValueRef, 16> arguments;
+ arguments.reserveInitialCapacity(argumentCount);
+ for (size_t i = 0; i < argumentCount; ++i)
+ arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
JSValueRef exception = 0;
JSValueRef result;
diff --git a/Source/JavaScriptCore/API/JSCallbackFunction.h b/Source/JavaScriptCore/API/JSCallbackFunction.h
index 50630b550..885ef949d 100644
--- a/Source/JavaScriptCore/API/JSCallbackFunction.h
+++ b/Source/JavaScriptCore/API/JSCallbackFunction.h
@@ -33,31 +33,27 @@ namespace JSC {
class JSCallbackFunction : public InternalFunction {
protected:
- JSCallbackFunction(JSGlobalObject*, JSObjectCallAsFunctionCallback);
- void finishCreation(JSGlobalData&, const String& name);
+ JSCallbackFunction(JSGlobalObject*, Structure*, JSObjectCallAsFunctionCallback);
+ void finishCreation(VM&, const String& name);
public:
typedef InternalFunction Base;
- static JSCallbackFunction* create(ExecState* exec, JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback callback, const String& name)
- {
- JSCallbackFunction* function = new (NotNull, allocateCell<JSCallbackFunction>(*exec->heap())) JSCallbackFunction(globalObject, callback);
- function->finishCreation(exec->globalData(), name);
- return function;
- }
+ static JSCallbackFunction* create(ExecState*, JSGlobalObject*, JSObjectCallAsFunctionCallback, const String& name);
static const ClassInfo s_info;
// InternalFunction mish-mashes constructor and function behavior -- we should
// refactor the code so this override isn't necessary
- static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
{
- return Structure::create(globalData, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
+ return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
}
-private:
+protected:
static CallType getCallData(JSCell*, CallData&);
+private:
static EncodedJSValue JSC_HOST_CALL call(ExecState*);
JSObjectCallAsFunctionCallback m_callback;
diff --git a/Source/JavaScriptCore/API/JSCallbackObject.cpp b/Source/JavaScriptCore/API/JSCallbackObject.cpp
index 76866feb0..7436e71f7 100644
--- a/Source/JavaScriptCore/API/JSCallbackObject.cpp
+++ b/Source/JavaScriptCore/API/JSCallbackObject.cpp
@@ -28,6 +28,7 @@
#include "JSCallbackObject.h"
#include "Heap.h"
+#include "Operations.h"
#include <wtf/text/StringHash.h>
namespace JSC {
@@ -40,30 +41,24 @@ template<> const bool JSCallbackObject<JSDestructibleObject>::needsDestruction =
template<> const bool JSCallbackObject<JSGlobalObject>::needsDestruction = false;
template<>
-JSCallbackObject<JSGlobalObject>* JSCallbackObject<JSGlobalObject>::create(JSGlobalData& globalData, JSClassRef classRef, Structure* structure)
+JSCallbackObject<JSGlobalObject>* JSCallbackObject<JSGlobalObject>::create(VM& vm, JSClassRef classRef, Structure* structure)
{
- JSCallbackObject<JSGlobalObject>* callbackObject = new (NotNull, allocateCell<JSCallbackObject<JSGlobalObject> >(globalData.heap)) JSCallbackObject(globalData, classRef, structure);
- callbackObject->finishCreation(globalData);
- globalData.heap.addFinalizer(callbackObject, destroy);
+ JSCallbackObject<JSGlobalObject>* callbackObject = new (NotNull, allocateCell<JSCallbackObject<JSGlobalObject> >(vm.heap)) JSCallbackObject(vm, classRef, structure);
+ callbackObject->finishCreation(vm);
+ vm.heap.addFinalizer(callbackObject, destroy);
return callbackObject;
}
template <>
-Structure* JSCallbackObject<JSDestructibleObject>::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
+Structure* JSCallbackObject<JSDestructibleObject>::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
{
- return Structure::create(globalData, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
+ return Structure::create(vm, globalObject, proto, TypeInfo(ObjectType, StructureFlags), &s_info);
}
template <>
-Structure* JSCallbackObject<JSGlobalObject>::createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue proto)
+Structure* JSCallbackObject<JSGlobalObject>::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
{
- return Structure::create(globalData, globalObject, proto, TypeInfo(GlobalObjectType, StructureFlags), &s_info);
-}
-
-template <class Parent>
-void JSCallbackObject<Parent>::destroy(JSCell* cell)
-{
- static_cast<JSCallbackObject*>(cell)->JSCallbackObject::~JSCallbackObject();
+ return Structure::create(vm, globalObject, proto, TypeInfo(GlobalObjectType, StructureFlags), &s_info);
}
void JSCallbackObjectData::finalize(Handle<Unknown> handle, void* context)
diff --git a/Source/JavaScriptCore/API/JSCallbackObject.h b/Source/JavaScriptCore/API/JSCallbackObject.h
index 3acf2ef10..16d8aa2c6 100644
--- a/Source/JavaScriptCore/API/JSCallbackObject.h
+++ b/Source/JavaScriptCore/API/JSCallbackObject.h
@@ -54,11 +54,11 @@ struct JSCallbackObjectData : WeakHandleOwner {
return m_privateProperties->getPrivateProperty(propertyName);
}
- void setPrivateProperty(JSGlobalData& globalData, JSCell* owner, const Identifier& propertyName, JSValue value)
+ void setPrivateProperty(VM& vm, JSCell* owner, const Identifier& propertyName, JSValue value)
{
if (!m_privateProperties)
m_privateProperties = adoptPtr(new JSPrivatePropertyMap);
- m_privateProperties->setPrivateProperty(globalData, owner, propertyName, value);
+ m_privateProperties->setPrivateProperty(vm, owner, propertyName, value);
}
void deletePrivateProperty(const Identifier& propertyName)
@@ -86,10 +86,10 @@ struct JSCallbackObjectData : WeakHandleOwner {
return location->value.get();
}
- void setPrivateProperty(JSGlobalData& globalData, JSCell* owner, const Identifier& propertyName, JSValue value)
+ void setPrivateProperty(VM& vm, JSCell* owner, const Identifier& propertyName, JSValue value)
{
WriteBarrier<Unknown> empty;
- m_propertyMap.add(propertyName.impl(), empty).iterator->value.set(globalData, owner, value);
+ m_propertyMap.add(propertyName.impl(), empty).iterator->value.set(vm, owner, value);
}
void deletePrivateProperty(const Identifier& propertyName)
@@ -118,10 +118,10 @@ template <class Parent>
class JSCallbackObject : public Parent {
protected:
JSCallbackObject(ExecState*, Structure*, JSClassRef, void* data);
- JSCallbackObject(JSGlobalData&, JSClassRef, Structure*);
+ JSCallbackObject(VM&, JSClassRef, Structure*);
void finishCreation(ExecState*);
- void finishCreation(JSGlobalData&);
+ void finishCreation(VM&);
public:
typedef Parent Base;
@@ -133,10 +133,13 @@ public:
callbackObject->finishCreation(exec);
return callbackObject;
}
- static JSCallbackObject<Parent>* create(JSGlobalData&, JSClassRef, Structure*);
+ static JSCallbackObject<Parent>* create(VM&, JSClassRef, Structure*);
static const bool needsDestruction;
- static void destroy(JSCell*);
+ static void destroy(JSCell* cell)
+ {
+ static_cast<JSCallbackObject*>(cell)->JSCallbackObject::~JSCallbackObject();
+ }
void setPrivate(void* data);
void* getPrivate();
@@ -146,16 +149,16 @@ public:
JSClassRef classRef() const { return m_callbackObjectData->jsClass; }
bool inherits(JSClassRef) const;
- static Structure* createStructure(JSGlobalData&, JSGlobalObject*, JSValue);
+ static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
JSValue getPrivateProperty(const Identifier& propertyName) const
{
return m_callbackObjectData->getPrivateProperty(propertyName);
}
- void setPrivateProperty(JSGlobalData& globalData, const Identifier& propertyName, JSValue value)
+ void setPrivateProperty(VM& vm, const Identifier& propertyName, JSValue value)
{
- m_callbackObjectData->setPrivateProperty(globalData, this, propertyName, value);
+ m_callbackObjectData->setPrivateProperty(vm, this, propertyName, value);
}
void deletePrivateProperty(const Identifier& propertyName)
@@ -178,6 +181,7 @@ private:
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
+ static void putByIndex(JSCell*, ExecState*, unsigned, JSValue, bool shouldThrow);
static bool deleteProperty(JSCell*, ExecState*, PropertyName);
static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned);
diff --git a/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h b/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
index 1a063e620..b92e3161e 100644
--- a/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
+++ b/Source/JavaScriptCore/API/JSCallbackObjectFunctions.h
@@ -51,7 +51,7 @@ inline JSCallbackObject<Parent>* JSCallbackObject<Parent>::asCallbackObject(JSVa
template <class Parent>
JSCallbackObject<Parent>::JSCallbackObject(ExecState* exec, Structure* structure, JSClassRef jsClass, void* data)
- : Parent(exec->globalData(), structure)
+ : Parent(exec->vm(), structure)
, m_callbackObjectData(adoptPtr(new JSCallbackObjectData(data, jsClass)))
{
}
@@ -59,8 +59,8 @@ JSCallbackObject<Parent>::JSCallbackObject(ExecState* exec, Structure* structure
// Global object constructor.
// FIXME: Move this into a separate JSGlobalCallbackObject class derived from this one.
template <class Parent>
-JSCallbackObject<Parent>::JSCallbackObject(JSGlobalData& globalData, JSClassRef jsClass, Structure* structure)
- : Parent(globalData, structure)
+JSCallbackObject<Parent>::JSCallbackObject(VM& vm, JSClassRef jsClass, Structure* structure)
+ : Parent(vm, structure)
, m_callbackObjectData(adoptPtr(new JSCallbackObjectData(0, jsClass)))
{
}
@@ -68,18 +68,18 @@ JSCallbackObject<Parent>::JSCallbackObject(JSGlobalData& globalData, JSClassRef
template <class Parent>
void JSCallbackObject<Parent>::finishCreation(ExecState* exec)
{
- Base::finishCreation(exec->globalData());
+ Base::finishCreation(exec->vm());
ASSERT(Parent::inherits(&s_info));
init(exec);
}
// This is just for Global object, so we can assume that Base::finishCreation is JSGlobalObject::finishCreation.
template <class Parent>
-void JSCallbackObject<Parent>::finishCreation(JSGlobalData& globalData)
+void JSCallbackObject<Parent>::finishCreation(VM& vm)
{
ASSERT(Parent::inherits(&s_info));
ASSERT(Parent::isGlobalObject());
- Base::finishCreation(globalData);
+ Base::finishCreation(vm);
init(jsCast<JSGlobalObject*>(this)->globalExec());
}
@@ -216,7 +216,7 @@ template <class Parent>
bool JSCallbackObject<Parent>::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
{
JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object);
- PropertySlot slot;
+ PropertySlot slot(thisObject);
if (thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot)) {
// Ideally we should return an access descriptor, but returning a value descriptor is better than nothing.
JSValue value = slot.getValue(exec, propertyName);
@@ -283,7 +283,7 @@ void JSCallbackObject<Parent>::put(JSCell* cell, ExecState* exec, PropertyName p
if (StaticFunctionEntry* entry = staticFunctions->get(name)) {
if (entry->attributes & kJSPropertyAttributeReadOnly)
return;
- thisObject->JSCallbackObject<Parent>::putDirect(exec->globalData(), propertyName, value); // put as override property
+ thisObject->JSCallbackObject<Parent>::putDirect(exec->vm(), propertyName, value); // put as override property
return;
}
}
@@ -294,6 +294,65 @@ void JSCallbackObject<Parent>::put(JSCell* cell, ExecState* exec, PropertyName p
}
template <class Parent>
+void JSCallbackObject<Parent>::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyIndex, JSValue value, bool shouldThrow)
+{
+ JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
+ JSContextRef ctx = toRef(exec);
+ JSObjectRef thisRef = toRef(thisObject);
+ RefPtr<OpaqueJSString> propertyNameRef;
+ JSValueRef valueRef = toRef(exec, value);
+ Identifier propertyName = Identifier(exec, String::number(propertyIndex));
+
+ for (JSClassRef jsClass = thisObject->classRef(); jsClass; jsClass = jsClass->parentClass) {
+ if (JSObjectSetPropertyCallback setProperty = jsClass->setProperty) {
+ if (!propertyNameRef)
+ propertyNameRef = OpaqueJSString::create(propertyName.impl());
+ JSValueRef exception = 0;
+ bool result;
+ {
+ APICallbackShim callbackShim(exec);
+ result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception);
+ }
+ if (exception)
+ throwError(exec, toJS(exec, exception));
+ if (result || exception)
+ return;
+ }
+
+ if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) {
+ if (StaticValueEntry* entry = staticValues->get(propertyName.impl())) {
+ if (entry->attributes & kJSPropertyAttributeReadOnly)
+ return;
+ if (JSObjectSetPropertyCallback setProperty = entry->setProperty) {
+ if (!propertyNameRef)
+ propertyNameRef = OpaqueJSString::create(propertyName.impl());
+ JSValueRef exception = 0;
+ bool result;
+ {
+ APICallbackShim callbackShim(exec);
+ result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception);
+ }
+ if (exception)
+ throwError(exec, toJS(exec, exception));
+ if (result || exception)
+ return;
+ }
+ }
+ }
+
+ if (OpaqueJSClassStaticFunctionsTable* staticFunctions = jsClass->staticFunctions(exec)) {
+ if (StaticFunctionEntry* entry = staticFunctions->get(propertyName.impl())) {
+ if (entry->attributes & kJSPropertyAttributeReadOnly)
+ return;
+ break;
+ }
+ }
+ }
+
+ return Parent::putByIndex(thisObject, exec, propertyIndex, value, shouldThrow);
+}
+
+template <class Parent>
bool JSCallbackObject<Parent>::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
{
JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(cell);
@@ -368,10 +427,11 @@ EncodedJSValue JSCallbackObject<Parent>::construct(ExecState* exec)
for (JSClassRef jsClass = jsCast<JSCallbackObject<Parent>*>(constructor)->classRef(); jsClass; jsClass = jsClass->parentClass) {
if (JSObjectCallAsConstructorCallback callAsConstructor = jsClass->callAsConstructor) {
- int argumentCount = static_cast<int>(exec->argumentCount());
- Vector<JSValueRef, 16> arguments(argumentCount);
- for (int i = 0; i < argumentCount; i++)
- arguments[i] = toRef(exec, exec->argument(i));
+ size_t argumentCount = exec->argumentCount();
+ Vector<JSValueRef, 16> arguments;
+ arguments.reserveInitialCapacity(argumentCount);
+ for (size_t i = 0; i < argumentCount; ++i)
+ arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
JSValueRef exception = 0;
JSObject* result;
{
@@ -384,7 +444,7 @@ EncodedJSValue JSCallbackObject<Parent>::construct(ExecState* exec)
}
}
- ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
+ RELEASE_ASSERT_NOT_REACHED(); // getConstructData should prevent us from reaching here
return JSValue::encode(JSValue());
}
@@ -434,10 +494,11 @@ EncodedJSValue JSCallbackObject<Parent>::call(ExecState* exec)
for (JSClassRef jsClass = jsCast<JSCallbackObject<Parent>*>(toJS(functionRef))->classRef(); jsClass; jsClass = jsClass->parentClass) {
if (JSObjectCallAsFunctionCallback callAsFunction = jsClass->callAsFunction) {
- int argumentCount = static_cast<int>(exec->argumentCount());
- Vector<JSValueRef, 16> arguments(argumentCount);
- for (int i = 0; i < argumentCount; i++)
- arguments[i] = toRef(exec, exec->argument(i));
+ size_t argumentCount = exec->argumentCount();
+ Vector<JSValueRef, 16> arguments;
+ arguments.reserveInitialCapacity(argumentCount);
+ for (size_t i = 0; i < argumentCount; ++i)
+ arguments.uncheckedAppend(toRef(exec, exec->argument(i)));
JSValueRef exception = 0;
JSValue result;
{
@@ -450,7 +511,7 @@ EncodedJSValue JSCallbackObject<Parent>::call(ExecState* exec)
}
}
- ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
+ RELEASE_ASSERT_NOT_REACHED(); // getCallData should prevent us from reaching here
return JSValue::encode(JSValue());
}
@@ -508,10 +569,10 @@ void* JSCallbackObject<Parent>::getPrivate()
template <class Parent>
bool JSCallbackObject<Parent>::inherits(JSClassRef c) const
{
- for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass)
+ for (JSClassRef jsClass = classRef(); jsClass; jsClass = jsClass->parentClass) {
if (jsClass == c)
return true;
-
+ }
return false;
}
@@ -566,7 +627,7 @@ JSValue JSCallbackObject<Parent>::staticFunctionGetter(ExecState* exec, JSValue
if (JSObjectCallAsFunctionCallback callAsFunction = entry->callAsFunction) {
JSObject* o = JSCallbackFunction::create(exec, thisObj->globalObject(), callAsFunction, name);
- thisObj->putDirect(exec->globalData(), propertyName, o, entry->attributes);
+ thisObj->putDirect(exec->vm(), propertyName, o, entry->attributes);
return o;
}
}
diff --git a/Source/JavaScriptCore/API/JSClassRef.cpp b/Source/JavaScriptCore/API/JSClassRef.cpp
index f30b02a2a..c77f63cf9 100644
--- a/Source/JavaScriptCore/API/JSClassRef.cpp
+++ b/Source/JavaScriptCore/API/JSClassRef.cpp
@@ -27,12 +27,13 @@
#include "JSClassRef.h"
#include "APICast.h"
+#include "Identifier.h"
+#include "InitializeThreading.h"
#include "JSCallbackObject.h"
+#include "JSGlobalObject.h"
#include "JSObjectRef.h"
-#include <runtime/InitializeThreading.h>
-#include <runtime/JSGlobalObject.h>
-#include <runtime/ObjectPrototype.h>
-#include <runtime/Identifier.h>
+#include "ObjectPrototype.h"
+#include "Operations.h"
#include <wtf/text/StringHash.h>
#include <wtf/unicode/UTF8.h>
@@ -126,7 +127,7 @@ PassRefPtr<OpaqueJSClass> OpaqueJSClass::create(const JSClassDefinition* clientD
return adoptRef(new OpaqueJSClass(&definition, protoClass.get()));
}
-OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::JSGlobalData&, OpaqueJSClass* jsClass)
+OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass* jsClass)
: m_class(jsClass)
{
if (jsClass->m_staticValues) {
@@ -150,9 +151,9 @@ OpaqueJSClassContextData::OpaqueJSClassContextData(JSC::JSGlobalData&, OpaqueJSC
OpaqueJSClassContextData& OpaqueJSClass::contextData(ExecState* exec)
{
- OwnPtr<OpaqueJSClassContextData>& contextData = exec->globalData().opaqueJSClassData.add(this, nullptr).iterator->value;
+ OwnPtr<OpaqueJSClassContextData>& contextData = exec->lexicalGlobalObject()->opaqueJSClassData().add(this, nullptr).iterator->value;
if (!contextData)
- contextData = adoptPtr(new OpaqueJSClassContextData(exec->globalData(), this));
+ contextData = adoptPtr(new OpaqueJSClassContextData(exec->vm(), this));
return *contextData;
}
@@ -202,7 +203,7 @@ JSObject* OpaqueJSClass::prototype(ExecState* exec)
JSObject* prototype = JSCallbackObject<JSDestructibleObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackObjectStructure(), prototypeClass, &jsClassData); // set jsClassData as the object's private data, so it can clear our reference on destruction
if (parentClass) {
if (JSObject* parentPrototype = parentClass->prototype(exec))
- prototype->setPrototype(exec->globalData(), parentPrototype);
+ prototype->setPrototype(exec->vm(), parentPrototype);
}
jsClassData.cachedPrototype = PassWeak<JSObject>(prototype);
diff --git a/Source/JavaScriptCore/API/JSClassRef.h b/Source/JavaScriptCore/API/JSClassRef.h
index 44d5d11b9..7bf18e758 100644
--- a/Source/JavaScriptCore/API/JSClassRef.h
+++ b/Source/JavaScriptCore/API/JSClassRef.h
@@ -26,10 +26,9 @@
#ifndef JSClassRef_h
#define JSClassRef_h
-#include "JSObjectRef.h"
+#include <JavaScriptCore/JSObjectRef.h>
#include "Weak.h"
-#include "JSObject.h"
#include "Protect.h"
#include <wtf/HashMap.h>
#include <wtf/text/WTFString.h>
@@ -69,14 +68,14 @@ struct OpaqueJSClass;
struct OpaqueJSClassContextData {
WTF_MAKE_NONCOPYABLE(OpaqueJSClassContextData); WTF_MAKE_FAST_ALLOCATED;
public:
- OpaqueJSClassContextData(JSC::JSGlobalData&, OpaqueJSClass*);
+ OpaqueJSClassContextData(JSC::VM&, OpaqueJSClass*);
// It is necessary to keep OpaqueJSClass alive because of the following rare scenario:
- // 1. A class is created and used, so its context data is stored in JSGlobalData hash map.
+ // 1. A class is created and used, so its context data is stored in VM hash map.
// 2. The class is released, and when all JS objects that use it are collected, OpaqueJSClass
// is deleted (that's the part prevented by this RefPtr).
// 3. Another class is created at the same address.
- // 4. When it is used, the old context data is found in JSGlobalData and used.
+ // 4. When it is used, the old context data is found in VM and used.
RefPtr<OpaqueJSClass> m_class;
OwnPtr<OpaqueJSClassStaticValuesTable> staticValues;
@@ -87,7 +86,7 @@ public:
struct OpaqueJSClass : public ThreadSafeRefCounted<OpaqueJSClass> {
static PassRefPtr<OpaqueJSClass> create(const JSClassDefinition*);
static PassRefPtr<OpaqueJSClass> createNoAutomaticPrototype(const JSClassDefinition*);
- ~OpaqueJSClass();
+ JS_EXPORT_PRIVATE ~OpaqueJSClass();
String className();
OpaqueJSClassStaticValuesTable* staticValues(JSC::ExecState*);
diff --git a/Source/JavaScriptCore/API/JSContext.h b/Source/JavaScriptCore/API/JSContext.h
new file mode 100644
index 000000000..ef3e51f17
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSContext.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSContext_h
+#define JSContext_h
+
+#include <JavaScriptCore/JavaScript.h>
+
+#if JSC_OBJC_API_ENABLED
+
+@class JSVirtualMachine, JSValue;
+
+// An instance of JSContext represents a JavaScript execution environment. All
+// JavaScript execution takes place within a context.
+// JSContext is also used to manage the life-cycle of objects within the
+// JavaScript virtual machine. Every instance of JSValue is associated with a
+// JSContext via a strong reference. The JSValue will keep the JSContext it
+// references alive so long as the JSValue remains alive. When all of the JSValues
+// that reference a particular JSContext have been deallocated the JSContext
+// will be deallocated unless it has been previously retained.
+
+NS_CLASS_AVAILABLE(10_9, NA)
+@interface JSContext : NSObject
+
+// Create a JSContext.
+- (id)init;
+// Create a JSContext in the specified virtual machine.
+- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;
+
+// Evaluate a string of JavaScript code.
+- (JSValue *)evaluateScript:(NSString *)script;
+
+// This method retrieves the global object of the JavaScript execution context.
+// Instances of JSContext originating from WebKit will return a reference to the
+// WindowProxy object.
+- (JSValue *)globalObject;
+
+// This method may be called from within an Objective-C block or method invoked
+// as a callback from JavaScript to retrieve the callback's context. Outside of
+// a callback from JavaScript this method will return nil.
++ (JSContext *)currentContext;
+// This method may be called from within an Objective-C block or method invoked
+// as a callback from JavaScript to retrieve the callback's this value. Outside
+// of a callback from JavaScript this method will return nil.
++ (JSValue *)currentThis;
+// This method may be called from within an Objective-C block or method invoked
+// as a callback from JavaScript to retrieve the callback's arguments, objects
+// in the returned array are instances of JSValue. Outside of a callback from
+// JavaScript this method will return nil.
++ (NSArray *)currentArguments;
+
+// The "exception" property may be used to throw an exception to JavaScript.
+// Before a callback is made from JavaScript to an Objective-C block or method,
+// the prior value of the exception property will be preserved and the property
+// will be set to nil. After the callback has completed the new value of the
+// exception property will be read, and prior value restored. If the new value
+// of exception is not nil, the callback will result in that value being thrown.
+// This property may also be used to check for uncaught exceptions arising from
+// API function calls (since the default behaviour of "exceptionHandler" is to
+// assign an uncaught exception to this property).
+// If a JSValue originating from a different JSVirtualMachine than this context
+// is assigned to this property, an Objective-C exception will be raised.
+@property(retain) JSValue *exception;
+
+// If a call to an API function results in an uncaught JavaScript exception, the
+// "exceptionHandler" block will be invoked. The default implementation for the
+// exception handler will store the exception to the exception property on
+// context. As a consequence the default behaviour is for unhandled exceptions
+// occurring within a callback from JavaScript to be rethrown upon return.
+// Setting this value to nil will result in all uncaught exceptions thrown from
+// the API being silently consumed.
+@property(copy) void(^exceptionHandler)(JSContext *context, JSValue *exception);
+
+// All instances of JSContext are associated with a single JSVirtualMachine. The
+// virtual machine provides an "object space" or set of execution resources.
+@property(readonly, retain) JSVirtualMachine *virtualMachine;
+
+@end
+
+// Instances of JSContext implement the following methods in order to enable
+// support for subscript access by key and index, for example:
+//
+// JSContext *context;
+// JSValue *v = context[@"X"]; // Get value for "X" from the global object.
+// context[@"Y"] = v; // Assign 'v' to "Y" on the global object.
+//
+// An object key passed as a subscript will be converted to a JavaScript value,
+// and then the value converted to a string used to resolve a property of the
+// global object.
+@interface JSContext(SubscriptSupport)
+
+- (JSValue *)objectForKeyedSubscript:(id)key;
+- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key;
+
+@end
+
+// These functions are for bridging between the C API and the Objective-C API.
+@interface JSContext(JSContextRefSupport)
+// Creates a JSContext, wrapping its C API counterpart.
++ (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)jsGlobalContextRef;
+// Returns the C API counterpart wrapped by a JSContext.
+- (JSGlobalContextRef)JSGlobalContextRef;
+@end
+
+#endif
+
+#endif // JSContext_h
diff --git a/Source/JavaScriptCore/API/JSContext.mm b/Source/JavaScriptCore/API/JSContext.mm
new file mode 100644
index 000000000..58754b38c
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSContext.mm
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#import "APICast.h"
+#import "APIShims.h"
+#import "JSContextInternal.h"
+#import "JSGlobalObject.h"
+#import "JSValueInternal.h"
+#import "JSVirtualMachineInternal.h"
+#import "JSWrapperMap.h"
+#import "JavaScriptCore.h"
+#import "ObjcRuntimeExtras.h"
+#import "Operations.h"
+#import "StrongInlines.h"
+#import <wtf/HashSet.h>
+
+#if JSC_OBJC_API_ENABLED
+
+@implementation JSContext {
+ JSVirtualMachine *m_virtualMachine;
+ JSGlobalContextRef m_context;
+ JSWrapperMap *m_wrapperMap;
+ JSC::Strong<JSC::JSObject> m_exception;
+}
+
+@synthesize exceptionHandler;
+
+- (JSGlobalContextRef)JSGlobalContextRef
+{
+ return m_context;
+}
+
+- (id)init
+{
+ return [self initWithVirtualMachine:[[[JSVirtualMachine alloc] init] autorelease]];
+}
+
+- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ m_virtualMachine = [virtualMachine retain];
+ m_context = JSGlobalContextCreateInGroup(getGroupFromVirtualMachine(virtualMachine), 0);
+ m_wrapperMap = [[JSWrapperMap alloc] initWithContext:self];
+
+ self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
+ context.exception = exceptionValue;
+ };
+
+ [m_virtualMachine addContext:self forGlobalContextRef:m_context];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [m_wrapperMap release];
+ JSGlobalContextRelease(m_context);
+ [m_virtualMachine release];
+ [self.exceptionHandler release];
+ [super dealloc];
+}
+
+- (JSValue *)evaluateScript:(NSString *)script
+{
+ JSValueRef exceptionValue = 0;
+ JSStringRef scriptJS = JSStringCreateWithCFString((CFStringRef)script);
+ JSValueRef result = JSEvaluateScript(m_context, scriptJS, 0, 0, 0, &exceptionValue);
+ JSStringRelease(scriptJS);
+
+ if (exceptionValue)
+ return [self valueFromNotifyException:exceptionValue];
+
+ return [JSValue valueWithJSValueRef:result inContext:self];
+}
+
+- (void)setException:(JSValue *)value
+{
+ if (value)
+ m_exception.set(toJS(m_context)->vm(), toJS(JSValueToObject(m_context, valueInternalValue(value), 0)));
+ else
+ m_exception.clear();
+}
+
+- (JSValue *)exception
+{
+ if (!m_exception)
+ return nil;
+ return [JSValue valueWithJSValueRef:toRef(m_exception.get()) inContext:self];
+}
+
+- (JSWrapperMap *)wrapperMap
+{
+ return m_wrapperMap;
+}
+
+- (JSValue *)globalObject
+{
+ return [JSValue valueWithJSValueRef:JSContextGetGlobalObject(m_context) inContext:self];
+}
+
++ (JSContext *)currentContext
+{
+ WTFThreadData& threadData = wtfThreadData();
+ CallbackData *entry = (CallbackData *)threadData.m_apiData;
+ return entry ? entry->context : nil;
+}
+
++ (JSValue *)currentThis
+{
+ WTFThreadData& threadData = wtfThreadData();
+ CallbackData *entry = (CallbackData *)threadData.m_apiData;
+
+ if (!entry->currentThis)
+ entry->currentThis = [[JSValue alloc] initWithValue:entry->thisValue inContext:[JSContext currentContext]];
+
+ return entry->currentThis;
+}
+
++ (NSArray *)currentArguments
+{
+ WTFThreadData& threadData = wtfThreadData();
+ CallbackData *entry = (CallbackData *)threadData.m_apiData;
+
+ if (!entry->currentArguments) {
+ JSContext *context = [JSContext currentContext];
+ size_t count = entry->argumentCount;
+ JSValue * argumentArray[count];
+ for (size_t i =0; i < count; ++i)
+ argumentArray[i] = [JSValue valueWithJSValueRef:entry->arguments[i] inContext:context];
+ entry->currentArguments = [[NSArray alloc] initWithObjects:argumentArray count:count];
+ }
+
+ return entry->currentArguments;
+}
+
+- (JSVirtualMachine *)virtualMachine
+{
+ return m_virtualMachine;
+}
+
+@end
+
+@implementation JSContext(SubscriptSupport)
+
+- (JSValue *)objectForKeyedSubscript:(id)key
+{
+ return [self globalObject][key];
+}
+
+- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
+{
+ [self globalObject][key] = object;
+}
+
+@end
+
+@implementation JSContext(Internal)
+
+- (id)initWithGlobalContextRef:(JSGlobalContextRef)context
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ JSC::JSGlobalObject* globalObject = toJS(context)->lexicalGlobalObject();
+ m_virtualMachine = [[JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&globalObject->vm())] retain];
+ ASSERT(m_virtualMachine);
+ m_context = JSGlobalContextRetain(context);
+ m_wrapperMap = [[JSWrapperMap alloc] initWithContext:self];
+
+ self.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
+ context.exception = exceptionValue;
+ };
+
+ [m_virtualMachine addContext:self forGlobalContextRef:m_context];
+
+ return self;
+}
+
+- (void)notifyException:(JSValueRef)exceptionValue
+{
+ self.exceptionHandler(self, [JSValue valueWithJSValueRef:exceptionValue inContext:self]);
+}
+
+- (JSValue *)valueFromNotifyException:(JSValueRef)exceptionValue
+{
+ [self notifyException:exceptionValue];
+ return [JSValue valueWithUndefinedInContext:self];
+}
+
+- (BOOL)boolFromNotifyException:(JSValueRef)exceptionValue
+{
+ [self notifyException:exceptionValue];
+ return NO;
+}
+
+- (void)beginCallbackWithData:(CallbackData *)callbackData thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments
+{
+ WTFThreadData& threadData = wtfThreadData();
+ [self retain];
+ CallbackData *prevStack = (CallbackData *)threadData.m_apiData;
+ *callbackData = (CallbackData){ prevStack, self, [self.exception retain], thisValue, nil, argumentCount, arguments, nil };
+ threadData.m_apiData = callbackData;
+ self.exception = nil;
+}
+
+- (void)endCallbackWithData:(CallbackData *)callbackData
+{
+ WTFThreadData& threadData = wtfThreadData();
+ self.exception = callbackData->preservedException;
+ [callbackData->preservedException release];
+ [callbackData->currentThis release];
+ [callbackData->currentArguments release];
+ threadData.m_apiData = callbackData->next;
+ [self release];
+}
+
+- (JSValue *)wrapperForObjCObject:(id)object
+{
+ // Lock access to m_wrapperMap
+ JSC::JSLockHolder lock(toJS(m_context));
+ return [m_wrapperMap jsWrapperForObject:object];
+}
+
+- (JSValue *)wrapperForJSObject:(JSValueRef)value
+{
+ JSC::JSLockHolder lock(toJS(m_context));
+ return [m_wrapperMap objcWrapperForJSValueRef:value];
+}
+
++ (JSContext *)contextWithJSGlobalContextRef:(JSGlobalContextRef)globalContext
+{
+ JSVirtualMachine *virtualMachine = [JSVirtualMachine virtualMachineWithContextGroupRef:toRef(&toJS(globalContext)->vm())];
+ JSContext *context = [virtualMachine contextForGlobalContextRef:globalContext];
+ if (!context)
+ context = [[[JSContext alloc] initWithGlobalContextRef:globalContext] autorelease];
+ return context;
+}
+
+@end
+
+WeakContextRef::WeakContextRef(JSContext *context)
+{
+ objc_initWeak(&m_weakContext, context);
+}
+
+WeakContextRef::~WeakContextRef()
+{
+ objc_destroyWeak(&m_weakContext);
+}
+
+JSContext * WeakContextRef::get()
+{
+ return objc_loadWeak(&m_weakContext);
+}
+
+void WeakContextRef::set(JSContext *context)
+{
+ objc_storeWeak(&m_weakContext, context);
+}
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSContextInternal.h b/Source/JavaScriptCore/API/JSContextInternal.h
new file mode 100644
index 000000000..d08e97d93
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSContextInternal.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSContextInternal_h
+#define JSContextInternal_h
+
+#import <JavaScriptCore/JavaScriptCore.h>
+
+#if JSC_OBJC_API_ENABLED
+
+#import <JavaScriptCore/JSContext.h>
+
+struct CallbackData {
+ CallbackData *next;
+ JSContext *context;
+ JSValue *preservedException;
+ JSValueRef thisValue;
+ JSValue *currentThis;
+ size_t argumentCount;
+ const JSValueRef *arguments;
+ NSArray *currentArguments;
+};
+
+class WeakContextRef {
+public:
+ WeakContextRef(JSContext * = nil);
+ ~WeakContextRef();
+
+ JSContext * get();
+ void set(JSContext *);
+
+private:
+ JSContext *m_weakContext;
+};
+
+@class JSWrapperMap;
+
+@interface JSContext(Internal)
+
+- (id)initWithGlobalContextRef:(JSGlobalContextRef)context;
+
+- (void)notifyException:(JSValueRef)exception;
+- (JSValue *)valueFromNotifyException:(JSValueRef)exception;
+- (BOOL)boolFromNotifyException:(JSValueRef)exception;
+
+- (void)beginCallbackWithData:(CallbackData *)callbackData thisValue:(JSValueRef)thisValue argumentCount:(size_t)argumentCount arguments:(const JSValueRef *)arguments;
+- (void)endCallbackWithData:(CallbackData *)callbackData;
+
+- (JSValue *)wrapperForObjCObject:(id)object;
+- (JSValue *)wrapperForJSObject:(JSValueRef)value;
+
+@property (readonly, retain) JSWrapperMap *wrapperMap;
+
+@end
+
+#endif
+
+#endif // JSContextInternal_h
diff --git a/Source/JavaScriptCore/API/JSContextRef.cpp b/Source/JavaScriptCore/API/JSContextRef.cpp
index 162c825aa..3869e87bf 100644
--- a/Source/JavaScriptCore/API/JSContextRef.cpp
+++ b/Source/JavaScriptCore/API/JSContextRef.cpp
@@ -35,6 +35,8 @@
#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>
@@ -54,7 +56,7 @@ using namespace JSC;
JSContextGroupRef JSContextGroupCreate()
{
initializeThreading();
- return toRef(JSGlobalData::createContextGroup().leakRef());
+ return toRef(VM::createContextGroup().leakRef());
}
JSContextGroupRef JSContextGroupRetain(JSContextGroupRef group)
@@ -65,7 +67,44 @@ 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.
@@ -75,10 +114,10 @@ JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
initializeThreading();
#if OS(DARWIN)
- // If the application was linked before JSGlobalContextCreate was changed to use a unique JSGlobalData,
+ // 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) {
- return JSGlobalContextCreateInGroup(toRef(&JSGlobalData::sharedInstance()), globalObjectClass);
+ return JSGlobalContextCreateInGroup(toRef(&VM::sharedInstance()), globalObjectClass);
}
#endif // OS(DARWIN)
@@ -89,22 +128,22 @@ JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClass
{
initializeThreading();
- RefPtr<JSGlobalData> globalData = group ? PassRefPtr<JSGlobalData>(toJS(group)) : JSGlobalData::createContextGroup();
+ RefPtr<VM> vm = group ? PassRefPtr<VM>(toJS(group)) : VM::createContextGroup();
- APIEntryShim entryShim(globalData.get(), false);
- globalData->makeUsableFromMultipleThreads();
+ APIEntryShim entryShim(vm.get(), false);
+ vm->makeUsableFromMultipleThreads();
if (!globalObjectClass) {
- JSGlobalObject* globalObject = JSGlobalObject::create(*globalData, JSGlobalObject::createStructure(*globalData, jsNull()));
+ JSGlobalObject* globalObject = JSGlobalObject::create(*vm, JSGlobalObject::createStructure(*vm, jsNull()));
return JSGlobalContextRetain(toGlobalRef(globalObject->globalExec()));
}
- JSGlobalObject* globalObject = JSCallbackObject<JSGlobalObject>::create(*globalData, globalObjectClass, JSCallbackObject<JSGlobalObject>::createStructure(*globalData, 0, 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));
}
@@ -113,9 +152,9 @@ JSGlobalContextRef JSGlobalContextRetain(JSGlobalContextRef ctx)
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
- JSGlobalData& globalData = exec->globalData();
+ VM& vm = exec->vm();
gcProtect(exec->dynamicGlobalObject());
- globalData.ref();
+ vm.ref();
return ctx;
}
@@ -126,13 +165,13 @@ void JSGlobalContextRelease(JSGlobalContextRef ctx)
{
JSLockHolder lock(exec);
- JSGlobalData& globalData = exec->globalData();
- savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(globalData.identifierTable);
+ VM& vm = exec->vm();
+ savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(vm.identifierTable);
bool protectCountIsZero = Heap::heap(exec->dynamicGlobalObject())->unprotect(exec->dynamicGlobalObject());
if (protectCountIsZero)
- globalData.heap.reportAbandonedObjectGraph();
- globalData.deref();
+ vm.heap.reportAbandonedObjectGraph();
+ vm.deref();
}
wtfThreadData().setCurrentIdentifierTable(savedIdentifierTable);
@@ -140,6 +179,10 @@ void JSGlobalContextRelease(JSGlobalContextRef ctx)
JSObjectRef JSContextGetGlobalObject(JSContextRef ctx)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -149,12 +192,20 @@ JSObjectRef JSContextGetGlobalObject(JSContextRef ctx)
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);
@@ -163,53 +214,46 @@ JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx)
JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
JSLockHolder lock(exec);
-
- unsigned count = 0;
StringBuilder builder;
- CallFrame* callFrame = exec;
- String functionName;
- if (exec->callee()) {
- if (asObject(exec->callee())->inherits(&InternalFunction::s_info)) {
- functionName = asInternalFunction(exec->callee())->name(exec);
- builder.appendLiteral("#0 ");
- builder.append(functionName);
- builder.appendLiteral("() ");
- count++;
- }
- }
- while (true) {
- ASSERT(callFrame);
- int signedLineNumber;
- intptr_t sourceID;
- String urlString;
- JSValue function;
+ Vector<StackFrame> stackTrace;
+ Interpreter::getStackTrace(&exec->vm(), stackTrace, maxStackSize);
- exec->interpreter()->retrieveLastCaller(callFrame, signedLineNumber, sourceID, urlString, function);
-
- if (function)
- functionName = jsCast<JSFunction*>(function)->name(exec);
+ 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.appendNumber(count);
+ builder.appendNumber(i);
builder.append(' ');
builder.append(functionName);
builder.appendLiteral("() at ");
builder.append(urlString);
- builder.append(':');
- builder.appendNumber(lineNumber);
- if (!function || ++count == maxStackSize)
+ if (frame.codeType != StackFrameNativeCode) {
+ builder.append(':');
+ builder.appendNumber(lineNumber);
+ }
+ if (!function)
break;
- callFrame = callFrame->callerFrame();
}
return OpaqueJSString::create(builder.toString()).leakRef();
}
diff --git a/Source/JavaScriptCore/API/JSContextRefPrivate.h b/Source/JavaScriptCore/API/JSContextRefPrivate.h
index 4f77aeada..8d7684ac0 100644
--- a/Source/JavaScriptCore/API/JSContextRefPrivate.h
+++ b/Source/JavaScriptCore/API/JSContextRefPrivate.h
@@ -55,6 +55,54 @@ JS_EXPORT JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx);
*/
JS_EXPORT JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize) AVAILABLE_IN_WEBKIT_VERSION_4_0;
+
+/*!
+@typedef JSShouldTerminateCallback
+@abstract The callback invoked when script execution has exceeded the allowed
+ time limit previously specified via JSContextGroupSetExecutionTimeLimit.
+@param ctx The execution context to use.
+@param context User specified context data previously passed to
+ JSContextGroupSetExecutionTimeLimit.
+@discussion If you named your function Callback, you would declare it like this:
+
+ bool Callback(JSContextRef ctx, void* context);
+
+ If you return true, the timed out script will terminate.
+ If you return false, the script will run for another period of the allowed
+ time limit specified via JSContextGroupSetExecutionTimeLimit.
+
+ Within this callback function, you may call JSContextGroupSetExecutionTimeLimit
+ to set a new time limit, or JSContextGroupClearExecutionTimeLimit to cancel the
+ timeout.
+*/
+typedef bool
+(*JSShouldTerminateCallback) (JSContextRef ctx, void* context);
+
+/*!
+@function
+@abstract Sets the script execution time limit.
+@param group The JavaScript context group that this time limit applies to.
+@param limit The time limit of allowed script execution time in seconds.
+@param callback The callback function that will be invoked when the time limit
+ has been reached. This will give you a chance to decide if you want to
+ terminate the script or not. If you pass a NULL callback, the script will be
+ terminated unconditionally when the time limit has been reached.
+@param context User data that you can provide to be passed back to you
+ in your callback.
+
+ In order to guarantee that the execution time limit will take effect, you will
+ need to call JSContextGroupSetExecutionTimeLimit before you start executing
+ any scripts.
+*/
+JS_EXPORT void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef, double limit, JSShouldTerminateCallback, void* context) AVAILABLE_IN_WEBKIT_VERSION_4_0;
+
+/*!
+@function
+@abstract Clears the script execution time limit.
+@param group The JavaScript context group that the time limit is cleared on.
+*/
+JS_EXPORT void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef) AVAILABLE_IN_WEBKIT_VERSION_4_0;
+
#ifdef __cplusplus
}
#endif
diff --git a/Source/JavaScriptCore/API/JSExport.h b/Source/JavaScriptCore/API/JSExport.h
new file mode 100644
index 000000000..96e9fec35
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSExport.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <JavaScriptCore/JavaScriptCore.h>
+
+#if JSC_OBJC_API_ENABLED
+
+// When a JavaScript value is created from an instance of an Objective-C class
+// for which no copying conversion is specified a JavaScript wrapper object will
+// be created.
+//
+// In JavaScript inheritance is supported via a chain of prototype objects, and
+// for each Objective-C class (and per JSContext) an object appropriate for use
+// as a prototype will be provided. For the class NSObject the prototype object
+// will be the JavaScript context's Object Prototype. For all other Objective-C
+// classes a Prototype object will be created. The Prototype object for a given
+// Objective-C class will have its internal [Prototype] property set to point to
+// the Prototype object of the Objective-C class's superclass. As such the
+// prototype chain for a JavaScript wrapper object will reflect the wrapped
+// Objective-C type's inheritance hierarchy.
+//
+// In addition to the Prototype object a JavaScript Constructor object will also
+// be produced for each Objective-C class. The Constructor object has a property
+// named 'prototype' that references the Prototype object, and the Prototype
+// object has a property named 'constructor' that references the Constructor.
+// The Constructor object is not callable.
+//
+// By default no methods or properties of the Objective-C class will be exposed
+// to JavaScript, however methods and properties may explicitly be exported.
+// For each protocol that a class conforms to, if the protocol incorporates the
+// protocol JSExport, then the protocol will be interpreted as a list of methods
+// and properties to be exported to JavaScript.
+//
+// For each instance method being exported, a corresponding JavaScript function
+// will be assigned as a property of the Prototype object, for each Objective-C
+// property being exported a JavaScript accessor property will be created on the
+// Prototype, and for each class method exported a JavaScript function will be
+// created on the Constructor object. For example:
+//
+// @protocol MyClassJavaScriptMethods <JSExport>
+// - (void)foo;
+// @end
+//
+// @interface MyClass : NSObject <MyClassJavaScriptMethods>
+// - (void)foo;
+// - (void)bar;
+// @end
+//
+// Data properties that are created on the prototype or constructor objects have
+// the attributes: writable:true, enumerable:false, configurable:true. Accessor
+// properties have the attributes: enumerable:false and configurable:true.
+//
+// If an instance of MyClass is converted to a JavaScript value, the resulting
+// wrapper object will (via its prototype) export the method "foo" to JavaScript,
+// since the class conforms to the MyClassJavaScriptMethods protocol, and this
+// protocol incorporates JSExport. "bar" will not be exported.
+//
+// Properties, arguments, and return values of the following types are
+// supported:
+//
+// Primitive numbers: signed values of up to 32-bits are converted in a manner
+// consistent with valueWithInt32/toInt32, unsigned values of up to 32-bits
+// are converted in a manner consistent with valueWithUInt32/toUInt32, all
+// other numeric values are converted consistently with valueWithDouble/
+// toDouble.
+// BOOL: values are converted consistently with valueWithBool/toBool.
+// id: values are converted consistently with valueWithObject/toObject.
+// <Objective-C Class>: - where the type is a pointer to a specified Objective-C
+// class, conversion is consistent with valueWithObjectOfClass/toObject.
+// struct types: C struct types are supported, where JSValue provides support
+// for the given type. Support is built in for CGPoint, NSRange, CGRect, and
+// CGSize.
+// block types: In addition to support provided by valueWithObject/toObject for
+// block types, if a JavaScript Function is passed as an argument, where the
+// type required is a block with a void return value (and where the block's
+// arguments are all of supported types), then a special adaptor block
+// will be created, allowing the JavaScript function to be used in the place
+// of a block.
+//
+// For any interface that conforms to JSExport the normal copying conversion for
+// built in types will be inhibited - so, for example, if an instance that
+// derives from NSString but conforms to JSExport is passed to valueWithObject:
+// then a wrapper object for the Objective-C object will be returned rather than
+// a JavaScript string primitive.
+@protocol JSExport
+@end
+
+// When a selector that takes one or more arguments is converted to a JavaScript
+// property name, by default a property name will be generated by performing the
+// following conversion:
+// - All colons are removed from the selector
+// - Any lowercase letter that had followed a colon will be capitalized.
+// Under the default conversion a selector "doFoo:withBar:" will be exported as
+// "doFooWithBar". The default conversion may be overriden using the JSExportAs
+// macro, for example to export a method "doFoo:withBar:" as "doFoo":
+//
+// @protocol MyClassJavaScriptMethods <JSExport>
+// JSExportAs(doFoo,
+// - (void)doFoo:(id)foo withBar:(id)bar
+// );
+// @end
+//
+// Note that the JSExport macro may only be applied to a selector that takes one
+// or more argument.
+#define JSExportAs(PropertyName, Selector) \
+ @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSManagedValue.h b/Source/JavaScriptCore/API/JSManagedValue.h
new file mode 100644
index 000000000..5ff3f83cb
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSManagedValue.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSManagedValue_h
+#define JSManagedValue_h
+
+#import <JavaScriptCore/JSBase.h>
+
+#if JSC_OBJC_API_ENABLED
+
+@class JSValue;
+@class JSContext;
+
+// JSManagedValue represents a "conditionally retained" JSValue.
+// "Conditionally retained" means that as long as either the JSManagedValue
+// JavaScript value is reachable through the JavaScript object graph
+// or the JSManagedValue object is reachable through the external Objective-C
+// object graph as reported to the JSVirtualMachine using
+// addManagedReference:withOwner:, the corresponding JavaScript value will
+// be retained. However, if neither of these conditions are true, the
+// corresponding JSValue will be released and set to nil.
+//
+// The primary use case for JSManagedValue is for safely referencing JSValues
+// from the Objective-C heap. It is incorrect to store a JSValue into an
+// Objective-C heap object, as this can very easily create a reference cycle,
+// keeping the entire JSContext alive.
+NS_CLASS_AVAILABLE(10_9, NA)
+@interface JSManagedValue : NSObject
+
+// Convenience method for creating JSManagedValues from JSValues.
++ (JSManagedValue *)managedValueWithValue:(JSValue *)value;
+
+// Create a JSManagedValue.
+- (id)initWithValue:(JSValue *)value;
+
+// Get the JSValue to which this JSManagedValue refers. If the JavaScript value has been collected,
+// this method returns nil.
+- (JSValue *)value;
+
+@end
+
+#endif // JSC_OBJC_API_ENABLED
+
+#endif // JSManagedValue_h
diff --git a/Source/JavaScriptCore/API/JSManagedValue.mm b/Source/JavaScriptCore/API/JSManagedValue.mm
new file mode 100644
index 000000000..f336ba662
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSManagedValue.mm
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#import "config.h"
+#import "JSManagedValue.h"
+
+#if JSC_OBJC_API_ENABLED
+
+#import "APICast.h"
+#import "Heap.h"
+#import "JSCJSValueInlines.h"
+#import "JSContextInternal.h"
+#import "JSValueInternal.h"
+#import "Weak.h"
+#import "WeakHandleOwner.h"
+#import "ObjcRuntimeExtras.h"
+
+class JSManagedValueHandleOwner : public JSC::WeakHandleOwner {
+public:
+ virtual void finalize(JSC::Handle<JSC::Unknown>, void* context);
+ virtual bool isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor&);
+};
+
+static JSManagedValueHandleOwner* managedValueHandleOwner()
+{
+ DEFINE_STATIC_LOCAL(JSManagedValueHandleOwner, jsManagedValueHandleOwner, ());
+ return &jsManagedValueHandleOwner;
+}
+
+@implementation JSManagedValue {
+ JSC::Weak<JSC::JSObject> m_value;
+}
+
++ (JSManagedValue *)managedValueWithValue:(JSValue *)value
+{
+ return [[[self alloc] initWithValue:value] autorelease];
+}
+
+- (id)init
+{
+ return [self initWithValue:nil];
+}
+
+- (id)initWithValue:(JSValue *)value
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ if (!value || !JSValueIsObject([value.context JSGlobalContextRef], [value JSValueRef])) {
+ JSC::Weak<JSC::JSObject> weak;
+ m_value.swap(weak);
+ } else {
+ JSC::JSObject* object = toJS(const_cast<OpaqueJSValue*>([value JSValueRef]));
+ JSC::Weak<JSC::JSObject> weak(object, managedValueHandleOwner(), self);
+ m_value.swap(weak);
+ }
+
+ return self;
+}
+
+- (JSValue *)value
+{
+ if (!m_value)
+ return nil;
+ JSC::JSObject* object = m_value.get();
+ JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(object->structure()->globalObject()->globalExec())];
+ return [JSValue valueWithJSValueRef:toRef(object) inContext:context];
+}
+
+- (void)disconnectValue
+{
+ m_value.clear();
+}
+
+@end
+
+@interface JSManagedValue (PrivateMethods)
+- (void)disconnectValue;
+@end
+
+bool JSManagedValueHandleOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown>, void* context, JSC::SlotVisitor& visitor)
+{
+ JSManagedValue *managedValue = static_cast<JSManagedValue *>(context);
+ return visitor.containsOpaqueRoot(managedValue);
+}
+
+void JSManagedValueHandleOwner::finalize(JSC::Handle<JSC::Unknown>, void* context)
+{
+ JSManagedValue *managedValue = static_cast<JSManagedValue *>(context);
+ [managedValue disconnectValue];
+}
+
+#endif // JSC_OBJC_API_ENABLED
diff --git a/Source/JavaScriptCore/API/JSObjectRef.cpp b/Source/JavaScriptCore/API/JSObjectRef.cpp
index c62efc60d..5ba446513 100644
--- a/Source/JavaScriptCore/API/JSObjectRef.cpp
+++ b/Source/JavaScriptCore/API/JSObjectRef.cpp
@@ -37,6 +37,7 @@
#include "FunctionConstructor.h"
#include "Identifier.h"
#include "InitializeThreading.h"
+#include "JSAPIWrapperObject.h"
#include "JSArray.h"
#include "JSCallbackConstructor.h"
#include "JSCallbackFunction.h"
@@ -48,7 +49,9 @@
#include "JSRetainPtr.h"
#include "JSString.h"
#include "JSValueRef.h"
+#include "ObjectConstructor.h"
#include "ObjectPrototype.h"
+#include "Operations.h"
#include "PropertyNameArray.h"
#include "RegExpConstructor.h"
@@ -77,6 +80,10 @@ void JSClassRelease(JSClassRef jsClass)
JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, void* data)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -85,13 +92,17 @@ JSObjectRef JSObjectMake(JSContextRef ctx, JSClassRef jsClass, void* data)
JSCallbackObject<JSDestructibleObject>* object = JSCallbackObject<JSDestructibleObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackObjectStructure(), jsClass, data);
if (JSObject* prototype = jsClass->prototype(exec))
- object->setPrototype(exec->globalData(), prototype);
+ object->setPrototype(exec->vm(), prototype);
return toRef(object);
}
JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name, JSObjectCallAsFunctionCallback callAsFunction)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
return toRef(JSCallbackFunction::create(exec, exec->lexicalGlobalObject(), callAsFunction, name ? name->string() : ASCIILiteral("anonymous")));
@@ -99,6 +110,10 @@ JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name,
JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -107,16 +122,20 @@ JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObje
jsPrototype = exec->lexicalGlobalObject()->objectPrototype();
JSCallbackConstructor* constructor = JSCallbackConstructor::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->callbackConstructorStructure(), jsClass, callAsConstructor);
- constructor->putDirect(exec->globalData(), exec->propertyNames().prototype, jsPrototype, DontEnum | DontDelete | ReadOnly);
+ constructor->putDirect(exec->vm(), exec->propertyNames().prototype, jsPrototype, DontEnum | DontDelete | ReadOnly);
return toRef(constructor);
}
JSObjectRef JSObjectMakeFunction(JSContextRef ctx, JSStringRef name, unsigned parameterCount, const JSStringRef parameterNames[], JSStringRef body, JSStringRef sourceURL, int startingLineNumber, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
- Identifier nameID = name ? name->identifier(&exec->globalData()) : Identifier(exec, "anonymous");
+ Identifier nameID = name ? name->identifier(&exec->vm()) : Identifier(exec, "anonymous");
MarkedArgumentBuffer args;
for (unsigned i = 0; i < parameterCount; i++)
@@ -135,6 +154,10 @@ JSObjectRef JSObjectMakeFunction(JSContextRef ctx, JSStringRef name, unsigned pa
JSObjectRef JSObjectMakeArray(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -160,6 +183,10 @@ JSObjectRef JSObjectMakeArray(JSContextRef ctx, size_t argumentCount, const JSVa
JSObjectRef JSObjectMakeDate(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -180,6 +207,10 @@ JSObjectRef JSObjectMakeDate(JSContextRef ctx, size_t argumentCount, const JSVal
JSObjectRef JSObjectMakeError(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -199,6 +230,10 @@ JSObjectRef JSObjectMakeError(JSContextRef ctx, size_t argumentCount, const JSVa
JSObjectRef JSObjectMakeRegExp(JSContextRef ctx, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -219,6 +254,10 @@ JSObjectRef JSObjectMakeRegExp(JSContextRef ctx, size_t argumentCount, const JSV
JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -228,33 +267,45 @@ JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object)
void JSObjectSetPrototype(JSContextRef ctx, JSObjectRef object, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
JSValue jsValue = toJS(exec, value);
- jsObject->setPrototypeWithCycleCheck(exec->globalData(), jsValue.isObject() ? jsValue : jsNull());
+ jsObject->setPrototypeWithCycleCheck(exec->vm(), jsValue.isObject() ? jsValue : jsNull());
}
bool JSObjectHasProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
- return jsObject->hasProperty(exec, propertyName->identifier(&exec->globalData()));
+ return jsObject->hasProperty(exec, propertyName->identifier(&exec->vm()));
}
JSValueRef JSObjectGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
- JSValue jsValue = jsObject->get(exec, propertyName->identifier(&exec->globalData()));
+ JSValue jsValue = jsObject->get(exec, propertyName->identifier(&exec->vm()));
if (exec->hadException()) {
if (exception)
*exception = toRef(exec, exec->exception());
@@ -265,11 +316,15 @@ JSValueRef JSObjectGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef
void JSObjectSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
- Identifier name(propertyName->identifier(&exec->globalData()));
+ Identifier name(propertyName->identifier(&exec->vm()));
JSValue jsValue = toJS(exec, value);
if (attributes && !jsObject->hasProperty(exec, name))
@@ -288,6 +343,10 @@ void JSObjectSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef prope
JSValueRef JSObjectGetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -305,6 +364,10 @@ JSValueRef JSObjectGetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsi
void JSObjectSetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned propertyIndex, JSValueRef value, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -321,12 +384,16 @@ void JSObjectSetPropertyAtIndex(JSContextRef ctx, JSObjectRef object, unsigned p
bool JSObjectDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
- bool result = jsObject->methodTable()->deleteProperty(jsObject, exec, propertyName->identifier(&exec->globalData()));
+ bool result = jsObject->methodTable()->deleteProperty(jsObject, exec, propertyName->identifier(&exec->vm()));
if (exec->hadException()) {
if (exception)
*exception = toRef(exec, exec->exception());
@@ -337,19 +404,23 @@ bool JSObjectDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef pr
void* JSObjectGetPrivate(JSObjectRef object)
{
- JSObject* jsObject = toJS(object);
+ JSObject* jsObject = uncheckedToJS(object);
if (jsObject->inherits(&JSCallbackObject<JSGlobalObject>::s_info))
return jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->getPrivate();
if (jsObject->inherits(&JSCallbackObject<JSDestructibleObject>::s_info))
return jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->getPrivate();
+#if JSC_OBJC_API_ENABLED
+ if (jsObject->inherits(&JSCallbackObject<JSAPIWrapperObject>::s_info))
+ return jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->getPrivate();
+#endif
return 0;
}
bool JSObjectSetPrivate(JSObjectRef object, void* data)
{
- JSObject* jsObject = toJS(object);
+ JSObject* jsObject = uncheckedToJS(object);
if (jsObject->inherits(&JSCallbackObject<JSGlobalObject>::s_info)) {
jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivate(data);
@@ -359,6 +430,12 @@ bool JSObjectSetPrivate(JSObjectRef object, void* data)
jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivate(data);
return true;
}
+#if JSC_OBJC_API_ENABLED
+ if (jsObject->inherits(&JSCallbackObject<JSAPIWrapperObject>::s_info)) {
+ jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->setPrivate(data);
+ return true;
+ }
+#endif
return false;
}
@@ -369,11 +446,15 @@ JSValueRef JSObjectGetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSSt
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
JSValue result;
- Identifier name(propertyName->identifier(&exec->globalData()));
+ Identifier name(propertyName->identifier(&exec->vm()));
if (jsObject->inherits(&JSCallbackObject<JSGlobalObject>::s_info))
result = jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->getPrivateProperty(name);
else if (jsObject->inherits(&JSCallbackObject<JSDestructibleObject>::s_info))
result = jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->getPrivateProperty(name);
+#if JSC_OBJC_API_ENABLED
+ else if (jsObject->inherits(&JSCallbackObject<JSAPIWrapperObject>::s_info))
+ result = jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->getPrivateProperty(name);
+#endif
return toRef(exec, result);
}
@@ -383,15 +464,21 @@ bool JSObjectSetPrivateProperty(JSContextRef ctx, JSObjectRef object, JSStringRe
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
JSValue jsValue = value ? toJS(exec, value) : JSValue();
- Identifier name(propertyName->identifier(&exec->globalData()));
+ Identifier name(propertyName->identifier(&exec->vm()));
if (jsObject->inherits(&JSCallbackObject<JSGlobalObject>::s_info)) {
- jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivateProperty(exec->globalData(), name, jsValue);
+ jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->setPrivateProperty(exec->vm(), name, jsValue);
return true;
}
if (jsObject->inherits(&JSCallbackObject<JSDestructibleObject>::s_info)) {
- jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivateProperty(exec->globalData(), name, jsValue);
+ jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->setPrivateProperty(exec->vm(), name, jsValue);
return true;
}
+#if JSC_OBJC_API_ENABLED
+ if (jsObject->inherits(&JSCallbackObject<JSAPIWrapperObject>::s_info)) {
+ jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->setPrivateProperty(exec->vm(), name, jsValue);
+ return true;
+ }
+#endif
return false;
}
@@ -400,7 +487,7 @@ bool JSObjectDeletePrivateProperty(JSContextRef ctx, JSObjectRef object, JSStrin
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* jsObject = toJS(object);
- Identifier name(propertyName->identifier(&exec->globalData()));
+ Identifier name(propertyName->identifier(&exec->vm()));
if (jsObject->inherits(&JSCallbackObject<JSGlobalObject>::s_info)) {
jsCast<JSCallbackObject<JSGlobalObject>*>(jsObject)->deletePrivateProperty(name);
return true;
@@ -409,11 +496,19 @@ bool JSObjectDeletePrivateProperty(JSContextRef ctx, JSObjectRef object, JSStrin
jsCast<JSCallbackObject<JSDestructibleObject>*>(jsObject)->deletePrivateProperty(name);
return true;
}
+#if JSC_OBJC_API_ENABLED
+ if (jsObject->inherits(&JSCallbackObject<JSAPIWrapperObject>::s_info)) {
+ jsCast<JSCallbackObject<JSAPIWrapperObject>*>(jsObject)->deletePrivateProperty(name);
+ return true;
+ }
+#endif
return false;
}
bool JSObjectIsFunction(JSContextRef, JSObjectRef object)
{
+ if (!object)
+ return false;
CallData callData;
JSCell* cell = toJS(object);
return cell->methodTable()->getCallData(cell, callData) != CallTypeNone;
@@ -424,6 +519,9 @@ JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObject
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
+ if (!object)
+ return 0;
+
JSObject* jsObject = toJS(object);
JSObject* jsThisObject = toJS(thisObject);
@@ -453,6 +551,8 @@ JSValueRef JSObjectCallAsFunction(JSContextRef ctx, JSObjectRef object, JSObject
bool JSObjectIsConstructor(JSContextRef, JSObjectRef object)
{
+ if (!object)
+ return false;
JSObject* jsObject = toJS(object);
ConstructData constructData;
return jsObject->methodTable()->getConstructData(jsObject, constructData) != ConstructTypeNone;
@@ -463,6 +563,9 @@ JSObjectRef JSObjectCallAsConstructor(JSContextRef ctx, JSObjectRef object, size
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
+ if (!object)
+ return 0;
+
JSObject* jsObject = toJS(object);
ConstructData constructData;
@@ -486,33 +589,37 @@ JSObjectRef JSObjectCallAsConstructor(JSContextRef ctx, JSObjectRef object, size
struct OpaqueJSPropertyNameArray {
WTF_MAKE_FAST_ALLOCATED;
public:
- OpaqueJSPropertyNameArray(JSGlobalData* globalData)
+ OpaqueJSPropertyNameArray(VM* vm)
: refCount(0)
- , globalData(globalData)
+ , vm(vm)
{
}
unsigned refCount;
- JSGlobalData* globalData;
+ VM* vm;
Vector<JSRetainPtr<JSStringRef> > array;
};
JSPropertyNameArrayRef JSObjectCopyPropertyNames(JSContextRef ctx, JSObjectRef object)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
JSObject* jsObject = toJS(object);
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
- JSGlobalData* globalData = &exec->globalData();
+ VM* vm = &exec->vm();
- JSPropertyNameArrayRef propertyNames = new OpaqueJSPropertyNameArray(globalData);
- PropertyNameArray array(globalData);
+ JSPropertyNameArrayRef propertyNames = new OpaqueJSPropertyNameArray(vm);
+ PropertyNameArray array(vm);
jsObject->methodTable()->getPropertyNames(jsObject, exec, array, ExcludeDontEnumProperties);
size_t size = array.size();
propertyNames->array.reserveInitialCapacity(size);
for (size_t i = 0; i < size; ++i)
- propertyNames->array.append(JSRetainPtr<JSStringRef>(Adopt, OpaqueJSString::create(array[i].string()).leakRef()));
+ propertyNames->array.uncheckedAppend(JSRetainPtr<JSStringRef>(Adopt, OpaqueJSString::create(array[i].string()).leakRef()));
return JSPropertyNameArrayRetain(propertyNames);
}
@@ -526,7 +633,7 @@ JSPropertyNameArrayRef JSPropertyNameArrayRetain(JSPropertyNameArrayRef array)
void JSPropertyNameArrayRelease(JSPropertyNameArrayRef array)
{
if (--array->refCount == 0) {
- APIEntryShim entryShim(array->globalData, false);
+ APIEntryShim entryShim(array->vm, false);
delete array;
}
}
@@ -544,6 +651,6 @@ JSStringRef JSPropertyNameArrayGetNameAtIndex(JSPropertyNameArrayRef array, size
void JSPropertyNameAccumulatorAddName(JSPropertyNameAccumulatorRef array, JSStringRef propertyName)
{
PropertyNameArray* propertyNames = toJS(array);
- APIEntryShim entryShim(propertyNames->globalData());
- propertyNames->add(propertyName->identifier(propertyNames->globalData()));
+ APIEntryShim entryShim(propertyNames->vm());
+ propertyNames->add(propertyName->identifier(propertyNames->vm()));
}
diff --git a/Source/JavaScriptCore/API/JSProfilerPrivate.cpp b/Source/JavaScriptCore/API/JSProfilerPrivate.cpp
index c83bc63bd..0405b4b26 100644
--- a/Source/JavaScriptCore/API/JSProfilerPrivate.cpp
+++ b/Source/JavaScriptCore/API/JSProfilerPrivate.cpp
@@ -27,20 +27,20 @@
#include "JSProfilerPrivate.h"
#include "APICast.h"
+#include "LegacyProfiler.h"
#include "OpaqueJSString.h"
-#include "Profiler.h"
using namespace JSC;
void JSStartProfiling(JSContextRef ctx, JSStringRef title)
{
- Profiler::profiler()->startProfiling(toJS(ctx), title->string());
+ LegacyProfiler::profiler()->startProfiling(toJS(ctx), title->string());
}
void JSEndProfiling(JSContextRef ctx, JSStringRef title)
{
ExecState* exec = toJS(ctx);
- Profiler* profiler = Profiler::profiler();
+ LegacyProfiler* profiler = LegacyProfiler::profiler();
profiler->stopProfiling(exec, title->string());
}
diff --git a/Source/JavaScriptCore/API/JSScriptRef.cpp b/Source/JavaScriptCore/API/JSScriptRef.cpp
new file mode 100644
index 000000000..8a5f3caf3
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSScriptRef.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "APICast.h"
+#include "APIShims.h"
+#include "Completion.h"
+#include "JSBasePrivate.h"
+#include "VM.h"
+#include "JSScriptRefPrivate.h"
+#include "OpaqueJSString.h"
+#include "Operations.h"
+#include "Parser.h"
+#include "SourceCode.h"
+#include "SourceProvider.h"
+
+using namespace JSC;
+
+struct OpaqueJSScript : public SourceProvider {
+public:
+ static WTF::PassRefPtr<OpaqueJSScript> create(VM* vm, const String& url, int startingLineNumber, const String& source)
+ {
+ return WTF::adoptRef(new OpaqueJSScript(vm, url, startingLineNumber, source));
+ }
+
+ const String& source() const OVERRIDE
+ {
+ return m_source;
+ }
+
+ VM* vm() const { return m_vm; }
+
+private:
+ OpaqueJSScript(VM* vm, const String& url, int startingLineNumber, const String& source)
+ : SourceProvider(url, TextPosition(OrdinalNumber::fromOneBasedInt(startingLineNumber), OrdinalNumber::first()))
+ , m_vm(vm)
+ , m_source(source)
+ {
+ }
+
+ ~OpaqueJSScript() { }
+
+ VM* m_vm;
+ String m_source;
+};
+
+static bool parseScript(VM* vm, const SourceCode& source, ParserError& error)
+{
+ return JSC::parse<JSC::ProgramNode>(vm, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error);
+}
+
+extern "C" {
+
+JSScriptRef JSScriptCreateReferencingImmortalASCIIText(JSContextGroupRef contextGroup, JSStringRef url, int startingLineNumber, const char* source, size_t length, JSStringRef* errorMessage, int* errorLine)
+{
+ VM* vm = toJS(contextGroup);
+ APIEntryShim entryShim(vm);
+ for (size_t i = 0; i < length; i++) {
+ if (!isASCII(source[i]))
+ return 0;
+ }
+
+ RefPtr<OpaqueJSScript> result = OpaqueJSScript::create(vm, url->string(), startingLineNumber, String(StringImpl::createFromLiteral(source, length)));
+
+ ParserError error;
+ if (!parseScript(vm, SourceCode(result), error)) {
+ if (errorMessage)
+ *errorMessage = OpaqueJSString::create(error.m_message).leakRef();
+ if (errorLine)
+ *errorLine = error.m_line;
+ return 0;
+ }
+
+ return result.release().leakRef();
+}
+
+JSScriptRef JSScriptCreateFromString(JSContextGroupRef contextGroup, JSStringRef url, int startingLineNumber, JSStringRef source, JSStringRef* errorMessage, int* errorLine)
+{
+ VM* vm = toJS(contextGroup);
+ APIEntryShim entryShim(vm);
+
+ RefPtr<OpaqueJSScript> result = OpaqueJSScript::create(vm, url->string(), startingLineNumber, source->string());
+
+ ParserError error;
+ if (!parseScript(vm, SourceCode(result), error)) {
+ if (errorMessage)
+ *errorMessage = OpaqueJSString::create(error.m_message).leakRef();
+ if (errorLine)
+ *errorLine = error.m_line;
+ return 0;
+ }
+
+ return result.release().leakRef();
+}
+
+void JSScriptRetain(JSScriptRef script)
+{
+ APIEntryShim entryShim(script->vm());
+ script->ref();
+}
+
+void JSScriptRelease(JSScriptRef script)
+{
+ APIEntryShim entryShim(script->vm());
+ script->deref();
+}
+
+JSValueRef JSScriptEvaluate(JSContextRef context, JSScriptRef script, JSValueRef thisValueRef, JSValueRef* exception)
+{
+ ExecState* exec = toJS(context);
+ APIEntryShim entryShim(exec);
+ if (script->vm() != &exec->vm()) {
+ RELEASE_ASSERT_NOT_REACHED();
+ return 0;
+ }
+ JSValue internalException;
+ JSValue thisValue = thisValueRef ? toJS(exec, thisValueRef) : jsUndefined();
+ JSValue result = evaluate(exec, SourceCode(script), thisValue, &internalException);
+ if (internalException) {
+ if (exception)
+ *exception = toRef(exec, internalException);
+ return 0;
+ }
+ ASSERT(result);
+ return toRef(exec, result);
+}
+
+}
diff --git a/Source/JavaScriptCore/API/JSScriptRefPrivate.h b/Source/JavaScriptCore/API/JSScriptRefPrivate.h
new file mode 100644
index 000000000..e1992052a
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSScriptRefPrivate.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSScriptRefPrivate_h
+#define JSScriptRefPrivate_h
+
+#include <JavaScriptCore/JSContextRef.h>
+#include <JavaScriptCore/JSStringRef.h>
+#include <JavaScriptCore/JSValueRef.h>
+
+/*! @typedef JSScriptRef A JavaScript script reference. */
+typedef struct OpaqueJSScript* JSScriptRef;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ @function
+ @abstract Creates a script reference from an ascii string, without copying or taking ownership of the string
+ @param contextGroup The context group the script is to be used in.
+ @param url The source url to be reported in errors and exceptions.
+ @param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions.
+ @param source The source string. This is required to be pure ASCII and to never be deallocated.
+ @param length The length of the source string.
+ @param errorMessage A pointer to a JSStringRef in which to store the parse error message if the source is not valid. Pass NULL if you do not care to store an error message.
+ @param errorLine A pointer to an int in which to store the line number of a parser error. Pass NULL if you do not care to store an error line.
+ @result A JSScriptRef for the provided source, or NULL if any non-ASCII character is found in source or if the source is not a valid JavaScript program. Ownership follows the Create Rule.
+ @discussion Use this function to create a reusable script reference with a constant
+ buffer as the backing string. The source string must outlive the global context.
+ */
+JS_EXPORT JSScriptRef JSScriptCreateReferencingImmortalASCIIText(JSContextGroupRef contextGroup, JSStringRef url, int startingLineNumber, const char* source, size_t length, JSStringRef* errorMessage, int* errorLine);
+
+/*!
+ @function
+ @abstract Creates a script reference from a string
+ @param contextGroup The context group the script is to be used in.
+ @param url The source url to be reported in errors and exceptions.
+ @param startingLineNumber An integer value specifying the script's starting line number in the file located at sourceURL. This is only used when reporting exceptions.
+ @param source The source string.
+ @param errorMessage A pointer to a JSStringRef in which to store the parse error message if the source is not valid. Pass NULL if you do not care to store an error message.
+ @param errorLine A pointer to an int in which to store the line number of a parser error. Pass NULL if you do not care to store an error line.
+ @result A JSScriptRef for the provided source, or NULL is the source is not a valid JavaScript program. Ownership follows the Create Rule.
+ */
+JS_EXPORT JSScriptRef JSScriptCreateFromString(JSContextGroupRef contextGroup, JSStringRef url, int startingLineNumber, JSStringRef source, JSStringRef* errorMessage, int* errorLine);
+
+/*!
+ @function
+ @abstract Retains a JavaScript script.
+ @param script The script to retain.
+ */
+JS_EXPORT void JSScriptRetain(JSScriptRef script);
+
+/*!
+ @function
+ @abstract Releases a JavaScript script.
+ @param script The script to release.
+ */
+JS_EXPORT void JSScriptRelease(JSScriptRef script);
+
+/*!
+ @function
+ @abstract Evaluates a JavaScript script.
+ @param ctx The execution context to use.
+ @param script The JSScript to evaluate.
+ @param thisValue The value to use as "this" when evaluating the script.
+ @param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
+ @result The JSValue that results from evaluating script, or NULL if an exception is thrown.
+ */
+JS_EXPORT JSValueRef JSScriptEvaluate(JSContextRef ctx, JSScriptRef script, JSValueRef thisValue, JSValueRef* exception);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* JSScriptRefPrivate_h */
diff --git a/Source/JavaScriptCore/API/JSStringRef.cpp b/Source/JavaScriptCore/API/JSStringRef.cpp
index 7f2168dc2..a03afed5b 100644
--- a/Source/JavaScriptCore/API/JSStringRef.cpp
+++ b/Source/JavaScriptCore/API/JSStringRef.cpp
@@ -25,6 +25,7 @@
#include "config.h"
#include "JSStringRef.h"
+#include "JSStringRefPrivate.h"
#include "InitializeThreading.h"
#include "OpaqueJSString.h"
@@ -55,10 +56,15 @@ JSStringRef JSStringCreateWithUTF8CString(const char* string)
}
}
- // Null string.
return OpaqueJSString::create().leakRef();
}
+JSStringRef JSStringCreateWithCharactersNoCopy(const JSChar* chars, size_t numChars)
+{
+ initializeThreading();
+ return OpaqueJSString::create(StringImpl::createWithoutCopying(chars, numChars)).leakRef();
+}
+
JSStringRef JSStringRetain(JSStringRef string)
{
string->ref();
diff --git a/Source/JavaScriptCore/API/JSStringRefCF.cpp b/Source/JavaScriptCore/API/JSStringRefCF.cpp
index fd72a593c..64d2d6251 100644
--- a/Source/JavaScriptCore/API/JSStringRefCF.cpp
+++ b/Source/JavaScriptCore/API/JSStringRefCF.cpp
@@ -30,7 +30,7 @@
#include "InitializeThreading.h"
#include "JSStringRef.h"
#include "OpaqueJSString.h"
-#include <runtime/JSValue.h>
+#include <runtime/JSCJSValue.h>
#include <wtf/OwnArrayPtr.h>
JSStringRef JSStringCreateWithCFString(CFStringRef string)
@@ -51,9 +51,9 @@ JSStringRef JSStringCreateWithCFString(CFStringRef string)
CFStringGetCharacters(string, CFRangeMake(0, length), buffer.get());
COMPILE_ASSERT(sizeof(UniChar) == sizeof(UChar), unichar_and_uchar_must_be_same_size);
return OpaqueJSString::create(reinterpret_cast<UChar*>(buffer.get()), length).leakRef();
- } else {
- return OpaqueJSString::create(static_cast<const LChar*>(0), 0).leakRef();
}
+
+ return OpaqueJSString::create(reinterpret_cast<const LChar*>(""), 0).leakRef();
}
CFStringRef JSStringCopyCFString(CFAllocatorRef alloc, JSStringRef string)
diff --git a/Source/JavaScriptCore/API/JSStringRefPrivate.h b/Source/JavaScriptCore/API/JSStringRefPrivate.h
new file mode 100644
index 000000000..f1db806ee
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSStringRefPrivate.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSStringRefPrivate_h
+#define JSStringRefPrivate_h
+
+#include <JavaScriptCore/JSStringRef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JS_EXPORT JSStringRef JSStringCreateWithCharactersNoCopy(const JSChar* chars, size_t numChars);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* JSStringRefPrivate_h */
diff --git a/Source/JavaScriptCore/API/JSStringRefQt.cpp b/Source/JavaScriptCore/API/JSStringRefQt.cpp
new file mode 100644
index 000000000..259bad8e6
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSStringRefQt.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSStringRefQt.h"
+
+#include "APICast.h"
+#include "InitializeThreading.h"
+#include "JSStringRef.h"
+#include "OpaqueJSString.h"
+#include <runtime/JSCJSValue.h>
+#include <wtf/OwnArrayPtr.h>
+
+QString JSStringCopyQString(JSStringRef string)
+{
+ return string->qString();
+}
+
+JSRetainPtr<JSStringRef> JSStringCreateWithQString(const QString& qString)
+{
+ RefPtr<OpaqueJSString> jsString = OpaqueJSString::create(qString);
+
+ if (jsString)
+ return JSRetainPtr<JSStringRef>(Adopt, jsString.release().leakRef());
+
+ return JSRetainPtr<JSStringRef>(Adopt, OpaqueJSString::create().leakRef());
+}
diff --git a/Source/JavaScriptCore/API/JSStringRefQt.h b/Source/JavaScriptCore/API/JSStringRefQt.h
new file mode 100644
index 000000000..63a059fdb
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSStringRefQt.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSStringRefQt_h
+#define JSStringRefQt_h
+
+#include "JSBase.h"
+#include "JSRetainPtr.h"
+#include <QString>
+
+/* QString convenience methods */
+
+/*!
+@function
+@abstract Creates a QString from a JavaScript string.
+@param string The JSString to copy into the new QString.
+@result A QString containing string.
+*/
+JS_EXPORT QString JSStringCopyQString(JSStringRef string);
+JS_EXPORT JSRetainPtr<JSStringRef> JSStringCreateWithQString(const QString&);
+
+#endif /* JSStringRefQt_h */
diff --git a/Source/JavaScriptCore/API/JSValue.h b/Source/JavaScriptCore/API/JSValue.h
new file mode 100644
index 000000000..c77707538
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSValue.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSValue_h
+#define JSValue_h
+
+#if JSC_OBJC_API_ENABLED
+
+@class JSContext;
+
+// A JSValue is a reference to a value within the JavaScript object space of a
+// JSVirtualMachine. All instances of JSValue originate from a JSContext and
+// hold a strong reference to this JSContext. As long as any value associated with
+// a particular JSContext is retained, that JSContext will remain alive.
+// Where an instance method is invoked upon a JSValue, and this returns another
+// JSValue, the returned JSValue will originate from the same JSContext as the
+// JSValue on which the method was invoked.
+//
+// For all methods taking arguments of type id, arguments will be converted
+// into a JavaScript value according to the conversion specified below.
+// All JavaScript values are associated with a particular JSVirtualMachine
+// (the associated JSVirtualMachine is available indirectly via the context
+// property). An instance of JSValue may only be passed as an argument to
+// methods on instances of JSValue and JSContext that belong to the same
+// JSVirtualMachine - passing a JSValue to a method on an object originating
+// from a different JSVirtualMachine will result in an Objective-C exception
+// being raised.
+//
+// Conversion between Objective-C and JavaScript types.
+//
+// When converting between JavaScript values and Objective-C objects a copy is
+// performed. Values of types listed below are copied to the corresponding
+// types on conversion in each direction. For NSDictionaries, entries in the
+// dictionary that are keyed by strings are copied onto a JavaScript object.
+// For dictionaries and arrays, conversion is recursive, with the same object
+// conversion being applied to all entries in the collection.
+//
+// Objective-C type | JavaScript type
+// --------------------+---------------------
+// nil | undefined
+// NSNull | null
+// NSString | string
+// NSNumber | number, boolean
+// NSDictionary | Object object
+// NSArray | Array object
+// NSDate | Date object
+// NSBlock * | Function object *
+// id ** | Wrapper object **
+// Class *** | Constructor object ***
+//
+// * Instances of NSBlock with supported arguments types will be presented to
+// JavaScript as a callable Function object. For more information on supported
+// argument types see JSExport.h. If a JavaScript Function originating from an
+// Objective-C block is converted back to an Objective-C object the block will
+// be returned. All other JavaScript functions will be converted in the same
+// manner as a JavaScript object of type Object.
+//
+// ** For Objective-C instances that do not derive from the set of types listed
+// above, a wrapper object to provide a retaining handle to the Objective-C
+// instance from JavaScript. For more information on these wrapper objects, see
+// JSExport.h. When a JavaScript wrapper object is converted back to Objective-C
+// the Objective-C instance being retained by the wrapper is returned.
+//
+// *** For Objective-C Class objects a constructor object containing exported
+// class methods will be returned. See JSExport.h for more information on
+// constructor objects.
+
+NS_CLASS_AVAILABLE(10_9, NA)
+@interface JSValue : NSObject
+
+// Create a JSValue by converting an Objective-C object.
++ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context;
+// Create a JavaScript value from an Objective-C primitive type.
++ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context;
++ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context;
++ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context;
++ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context;
+// Create a JavaScript value in this context.
++ (JSValue *)valueWithNewObjectInContext:(JSContext *)context;
++ (JSValue *)valueWithNewArrayInContext:(JSContext *)context;
++ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context;
++ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context;
++ (JSValue *)valueWithNullInContext:(JSContext *)context;
++ (JSValue *)valueWithUndefinedInContext:(JSContext *)context;
+
+// Convert this value to a corresponding Objective-C object, according to the
+// conversion specified above.
+- (id)toObject;
+// Convert this value to a corresponding Objective-C object, if the result is
+// not of the specified class then nil will be returned.
+- (id)toObjectOfClass:(Class)expectedClass;
+// The value is copied to a boolean according to the conversion specified by the
+// JavaScript language.
+- (BOOL)toBool;
+// The value is copied to a number according to the conversion specified by the
+// JavaScript language.
+- (double)toDouble;
+// The value is copied to an integer according to the conversion specified by
+// the JavaScript language.
+- (int32_t)toInt32;
+// The value is copied to an integer according to the conversion specified by
+// the JavaScript language.
+- (uint32_t)toUInt32;
+// If the value is a boolean, a NSNumber value of @YES or @NO will be returned.
+// For all other types the value will be copied to a number according to the
+// conversion specified by the JavaScript language.
+- (NSNumber *)toNumber;
+// The value is copied to a string according to the conversion specified by the
+// JavaScript language.
+- (NSString *)toString;
+// The value is converted to a number representing a time interval since 1970,
+// and a new NSDate instance is returned.
+- (NSDate *)toDate;
+// If the value is null or undefined then nil is returned.
+// If the value is not an object then a JavaScript TypeError will be thrown.
+// The property "length" is read from the object, converted to an unsigned
+// integer, and an NSArray of this size is allocated. Properties corresponding
+// to indicies within the array bounds will be copied to the array, with
+// Objective-C objects converted to equivalent JSValues as specified.
+- (NSArray *)toArray;
+// If the value is null or undefined then nil is returned.
+// If the value is not an object then a JavaScript TypeError will be thrown.
+// All enumerable properties of the object are copied to the dictionary, with
+// Objective-C objects converted to equivalent JSValues as specified.
+- (NSDictionary *)toDictionary;
+
+// Access a property from the value. This method will return the JavaScript value
+// 'undefined' if the property does not exist.
+- (JSValue *)valueForProperty:(NSString *)property;
+// Set a property on the value.
+- (void)setValue:(id)value forProperty:(NSString *)property;
+// Delete a property from the value, returns YES if deletion is successful.
+- (BOOL)deleteProperty:(NSString *)property;
+// Returns YES if property is present on the value.
+// This method has the same function as the JavaScript operator "in".
+- (BOOL)hasProperty:(NSString *)property;
+// This method may be used to create a data or accessor property on an object;
+// this method operates in accordance with the Object.defineProperty method in
+// the JavaScript language.
+- (void)defineProperty:(NSString *)property descriptor:(id)descriptor;
+
+// Access an indexed property from the value. This method will return the
+// JavaScript value 'undefined' if no property exists at that index.
+- (JSValue *)valueAtIndex:(NSUInteger)index;
+// Set an indexed property on the value. For JSValues that are JavaScript arrays,
+// indices greater than UINT_MAX - 1 will not affect the length of the array.
+- (void)setValue:(id)value atIndex:(NSUInteger)index;
+
+// All JavaScript values are precisely one of these types.
+- (BOOL)isUndefined;
+- (BOOL)isNull;
+- (BOOL)isBoolean;
+- (BOOL)isNumber;
+- (BOOL)isString;
+- (BOOL)isObject;
+
+// This method has the same function as the JavaScript operator "===".
+- (BOOL)isEqualToObject:(id)value;
+// This method has the same function as the JavaScript operator "==".
+- (BOOL)isEqualWithTypeCoercionToObject:(id)value;
+// This method has the same function as the JavaScript operator "instanceof".
+- (BOOL)isInstanceOf:(id)value;
+
+// Call this value as a function passing the specified arguments.
+- (JSValue *)callWithArguments:(NSArray *)arguments;
+// Call this value as a constructor passing the specified arguments.
+- (JSValue *)constructWithArguments:(NSArray *)arguments;
+// Access the property named "method" from this value; call the value resulting
+// from the property access as a function, passing this value as the "this"
+// value, and the specified arguments.
+- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;
+
+// The JSContext that this value originates from.
+@property(readonly, retain) JSContext *context;
+
+@end
+
+// Objective-C methods exported to JavaScript may have argument and/or return
+// values of struct types, provided that conversion to and from the struct is
+// supported by JSValue. Support is provided for any types where JSValue
+// contains both a class method "valueWith<Type>:inContext:", and and instance
+// method "to<Type>" - where the string "<Type>" in these selector names match,
+// with the first argument to the former being of the same struct type as the
+// return type of the latter.
+// Support is provided for structs of type CGPoint, NSRange, CGRect and CGSize.
+@interface JSValue(StructSupport)
+
+// This method returns a newly allocated JavaScript object containing properties
+// named "x" and "y", with values from the CGPoint.
++ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context;
+// This method returns a newly allocated JavaScript object containing properties
+// named "location" and "length", with values from the NSRange.
++ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context;
+// This method returns a newly allocated JavaScript object containing properties
+// named "x", "y", "width", and "height", with values from the CGRect.
++ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context;
+// This method returns a newly allocated JavaScript object containing properties
+// named "width" and "height", with values from the CGSize.
++ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context;
+
+// Convert a value to type CGPoint by reading properties named "x" and "y" from
+// this value, and converting the results to double.
+- (CGPoint)toPoint;
+// Convert a value to type NSRange by accessing properties named "location" and
+// "length" from this value converting the results to double.
+- (NSRange)toRange;
+// Convert a value to type CGRect by reading properties named "x", "y", "width",
+// and "height" from this value, and converting the results to double.
+- (CGRect)toRect;
+// Convert a value to type CGSize by accessing properties named "width" and
+// "height" from this value converting the results to double.
+- (CGSize)toSize;
+
+@end
+
+// Instances of JSValue implement the following methods in order to enable
+// support for subscript access by key and index, for example:
+//
+// JSValue *objectA, *objectB;
+// JSValue *v1 = object[@"X"]; // Get value for property "X" from 'object'.
+// JSValue *v2 = object[42]; // Get value for index 42 from 'object'.
+// object[@"Y"] = v1; // Assign 'v1' to property "Y" of 'object'.
+// object[101] = v2; // Assign 'v2' to index 101 of 'object'.
+//
+// An object key passed as a subscript will be converted to a JavaScript value,
+// and then the value converted to a string used as a property name.
+@interface JSValue(SubscriptSupport)
+
+- (JSValue *)objectForKeyedSubscript:(id)key;
+- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index;
+- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key;
+- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index;
+
+@end
+
+// These functions are for bridging between the C API and the Objective-C API.
+@interface JSValue(JSValueRefSupport)
+// Creates a JSValue, wrapping its C API counterpart.
++ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context;
+// Returns the C API counterpart wrapped by a JSContext.
+- (JSValueRef)JSValueRef;
+@end
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// These keys may assist in creating a property descriptor for use with the
+// defineProperty method on JSValue.
+// Property descriptors must fit one of three descriptions:
+// Data Descriptor:
+// - A descriptor containing one or both of the keys "value" and "writable",
+// and optionally containing one or both of the keys "enumerable" and
+// "configurable". A data descriptor may not contain either the "get" or
+// "set" key.
+// A data descriptor may be used to create or modify the attributes of a
+// data property on an object (replacing any existing accessor property).
+// Accessor Descriptor:
+// - A descriptor containing one or both of the keys "get" and "set", and
+// optionally containing one or both of the keys "enumerable" and
+// "configurable". An accessor descriptor may not contain either the "value"
+// or "writable" key.
+// An accessor descriptor may be used to create or modify the attributes of
+// an accessor property on an object (replacing any existing data property).
+// Generic Descriptor:
+// - A descriptor containing one or both of the keys "enumerable" and
+// "configurable". A generic descriptor may not contain any of the keys
+// "value", " writable", "get", or "set".
+// A generic descriptor may be used to modify the attributes of an existing
+// data or accessor property, or to create a new data property.
+JS_EXPORT extern NSString * const JSPropertyDescriptorWritableKey;
+JS_EXPORT extern NSString * const JSPropertyDescriptorEnumerableKey;
+JS_EXPORT extern NSString * const JSPropertyDescriptorConfigurableKey;
+JS_EXPORT extern NSString * const JSPropertyDescriptorValueKey;
+JS_EXPORT extern NSString * const JSPropertyDescriptorGetKey;
+JS_EXPORT extern NSString * const JSPropertyDescriptorSetKey;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
+#endif // JSValue_h
diff --git a/Source/JavaScriptCore/API/JSValue.mm b/Source/JavaScriptCore/API/JSValue.mm
new file mode 100644
index 000000000..e708cc674
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSValue.mm
@@ -0,0 +1,1131 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#import "APICast.h"
+#import "APIShims.h"
+#import "DateInstance.h"
+#import "Error.h"
+#import "JavaScriptCore.h"
+#import "JSContextInternal.h"
+#import "JSVirtualMachineInternal.h"
+#import "JSValueInternal.h"
+#import "JSWrapperMap.h"
+#import "ObjcRuntimeExtras.h"
+#import "Operations.h"
+#import "JSCJSValue.h"
+#import <wtf/HashMap.h>
+#import <wtf/HashSet.h>
+#import <wtf/Vector.h>
+#import <wtf/TCSpinLock.h>
+#import <wtf/text/WTFString.h>
+#import <wtf/text/StringHash.h>
+
+#if JSC_OBJC_API_ENABLED
+
+NSString * const JSPropertyDescriptorWritableKey = @"writable";
+NSString * const JSPropertyDescriptorEnumerableKey = @"enumerable";
+NSString * const JSPropertyDescriptorConfigurableKey = @"configurable";
+NSString * const JSPropertyDescriptorValueKey = @"value";
+NSString * const JSPropertyDescriptorGetKey = @"get";
+NSString * const JSPropertyDescriptorSetKey = @"set";
+
+@implementation JSValue {
+ JSValueRef m_value;
+}
+
+- (JSValueRef)JSValueRef
+{
+ return m_value;
+}
+
++ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:objectToValue(context, value) inContext:context];
+}
+
++ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSValueMakeBoolean([context JSGlobalContextRef], value) inContext:context];
+}
+
++ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
+}
+
++ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
+}
+
++ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSValueMakeNumber([context JSGlobalContextRef], value) inContext:context];
+}
+
++ (JSValue *)valueWithNewObjectInContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSObjectMake([context JSGlobalContextRef], 0, 0) inContext:context];
+}
+
++ (JSValue *)valueWithNewArrayInContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSObjectMakeArray([context JSGlobalContextRef], 0, NULL, 0) inContext:context];
+}
+
++ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context
+{
+ JSStringRef patternString = JSStringCreateWithCFString((CFStringRef)pattern);
+ JSStringRef flagsString = JSStringCreateWithCFString((CFStringRef)flags);
+ JSValueRef arguments[2] = { JSValueMakeString([context JSGlobalContextRef], patternString), JSValueMakeString([context JSGlobalContextRef], flagsString) };
+ JSStringRelease(patternString);
+ JSStringRelease(flagsString);
+
+ return [JSValue valueWithJSValueRef:JSObjectMakeRegExp([context JSGlobalContextRef], 2, arguments, 0) inContext:context];
+}
+
++ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context
+{
+ JSStringRef string = JSStringCreateWithCFString((CFStringRef)message);
+ JSValueRef argument = JSValueMakeString([context JSGlobalContextRef], string);
+ JSStringRelease(string);
+
+ return [JSValue valueWithJSValueRef:JSObjectMakeError([context JSGlobalContextRef], 1, &argument, 0) inContext:context];
+}
+
++ (JSValue *)valueWithNullInContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSValueMakeNull([context JSGlobalContextRef]) inContext:context];
+}
+
++ (JSValue *)valueWithUndefinedInContext:(JSContext *)context
+{
+ return [JSValue valueWithJSValueRef:JSValueMakeUndefined([context JSGlobalContextRef]) inContext:context];
+}
+
+- (id)toObject
+{
+ return valueToObject(_context, m_value);
+}
+
+- (id)toObjectOfClass:(Class)expectedClass
+{
+ id result = [self toObject];
+ return [result isKindOfClass:expectedClass] ? result : nil;
+}
+
+- (BOOL)toBool
+{
+ return JSValueToBoolean([_context JSGlobalContextRef], m_value);
+}
+
+- (double)toDouble
+{
+ JSValueRef exception = 0;
+ double result = JSValueToNumber([_context JSGlobalContextRef], m_value, &exception);
+ if (exception) {
+ [_context notifyException:exception];
+ return std::numeric_limits<double>::quiet_NaN();
+ }
+
+ return result;
+}
+
+- (int32_t)toInt32
+{
+ return JSC::toInt32([self toDouble]);
+}
+
+- (uint32_t)toUInt32
+{
+ return JSC::toUInt32([self toDouble]);
+}
+
+- (NSNumber *)toNumber
+{
+ JSValueRef exception = 0;
+ id result = valueToNumber([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ [_context notifyException:exception];
+ return result;
+}
+
+- (NSString *)toString
+{
+ JSValueRef exception = 0;
+ id result = valueToString([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ [_context notifyException:exception];
+ return result;
+}
+
+- (NSDate *)toDate
+{
+ JSValueRef exception = 0;
+ id result = valueToDate([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ [_context notifyException:exception];
+ return result;
+}
+
+- (NSArray *)toArray
+{
+ JSValueRef exception = 0;
+ id result = valueToArray([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ [_context notifyException:exception];
+ return result;
+}
+
+- (NSDictionary *)toDictionary
+{
+ JSValueRef exception = 0;
+ id result = valueToDictionary([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ [_context notifyException:exception];
+ return result;
+}
+
+- (JSValue *)valueForProperty:(NSString *)propertyName
+{
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
+ JSValueRef result = JSObjectGetProperty([_context JSGlobalContextRef], object, name, &exception);
+ JSStringRelease(name);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ return [JSValue valueWithJSValueRef:result inContext:_context];
+}
+
+- (void)setValue:(id)value forProperty:(NSString *)propertyName
+{
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception) {
+ [_context notifyException:exception];
+ return;
+ }
+
+ JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
+ JSObjectSetProperty([_context JSGlobalContextRef], object, name, objectToValue(_context, value), 0, &exception);
+ JSStringRelease(name);
+ if (exception) {
+ [_context notifyException:exception];
+ return;
+ }
+}
+
+- (BOOL)deleteProperty:(NSString *)propertyName
+{
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context boolFromNotifyException:exception];
+
+ JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
+ BOOL result = JSObjectDeleteProperty([_context JSGlobalContextRef], object, name, &exception);
+ JSStringRelease(name);
+ if (exception)
+ return [_context boolFromNotifyException:exception];
+
+ return result;
+}
+
+- (BOOL)hasProperty:(NSString *)propertyName
+{
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context boolFromNotifyException:exception];
+
+ JSStringRef name = JSStringCreateWithCFString((CFStringRef)propertyName);
+ BOOL result = JSObjectHasProperty([_context JSGlobalContextRef], object, name);
+ JSStringRelease(name);
+ return result;
+}
+
+- (void)defineProperty:(NSString *)property descriptor:(id)descriptor
+{
+ [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, property, descriptor ]];
+}
+
+- (JSValue *)valueAtIndex:(NSUInteger)index
+{
+ // Properties that are higher than an unsigned value can hold are converted to a double then inserted as a normal property.
+ // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in get().
+ if (index != (unsigned)index)
+ return [self valueForProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
+
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSValueRef result = JSObjectGetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ return [JSValue valueWithJSValueRef:result inContext:_context];
+}
+
+- (void)setValue:(id)value atIndex:(NSUInteger)index
+{
+ // Properties that are higher than an unsigned value can hold are converted to a double, then inserted as a normal property.
+ // Indices that are bigger than the max allowed index size (UINT_MAX - 1) will be handled internally in putByIndex().
+ if (index != (unsigned)index)
+ return [self setValue:value forProperty:[[JSValue valueWithDouble:index inContext:_context] toString]];
+
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception) {
+ [_context notifyException:exception];
+ return;
+ }
+
+ JSObjectSetPropertyAtIndex([_context JSGlobalContextRef], object, (unsigned)index, objectToValue(_context, value), &exception);
+ if (exception) {
+ [_context notifyException:exception];
+ return;
+ }
+}
+
+- (BOOL)isUndefined
+{
+ return JSValueIsUndefined([_context JSGlobalContextRef], m_value);
+}
+
+- (BOOL)isNull
+{
+ return JSValueIsNull([_context JSGlobalContextRef], m_value);
+}
+
+- (BOOL)isBoolean
+{
+ return JSValueIsBoolean([_context JSGlobalContextRef], m_value);
+}
+
+- (BOOL)isNumber
+{
+ return JSValueIsNumber([_context JSGlobalContextRef], m_value);
+}
+
+- (BOOL)isString
+{
+ return JSValueIsString([_context JSGlobalContextRef], m_value);
+}
+
+- (BOOL)isObject
+{
+ return JSValueIsObject([_context JSGlobalContextRef], m_value);
+}
+
+- (BOOL)isEqualToObject:(id)value
+{
+ return JSValueIsStrictEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value));
+}
+
+- (BOOL)isEqualWithTypeCoercionToObject:(id)value
+{
+ JSValueRef exception = 0;
+ BOOL result = JSValueIsEqual([_context JSGlobalContextRef], m_value, objectToValue(_context, value), &exception);
+ if (exception)
+ return [_context boolFromNotifyException:exception];
+
+ return result;
+}
+
+- (BOOL)isInstanceOf:(id)value
+{
+ JSValueRef exception = 0;
+ JSObjectRef constructor = JSValueToObject([_context JSGlobalContextRef], objectToValue(_context, value), &exception);
+ if (exception)
+ return [_context boolFromNotifyException:exception];
+
+ BOOL result = JSValueIsInstanceOfConstructor([_context JSGlobalContextRef], m_value, constructor, &exception);
+ if (exception)
+ return [_context boolFromNotifyException:exception];
+
+ return result;
+}
+
+- (JSValue *)callWithArguments:(NSArray *)argumentArray
+{
+ NSUInteger argumentCount = [argumentArray count];
+ JSValueRef arguments[argumentCount];
+ for (unsigned i = 0; i < argumentCount; ++i)
+ arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
+
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, 0, argumentCount, arguments, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ return [JSValue valueWithJSValueRef:result inContext:_context];
+}
+
+- (JSValue *)constructWithArguments:(NSArray *)argumentArray
+{
+ NSUInteger argumentCount = [argumentArray count];
+ JSValueRef arguments[argumentCount];
+ for (unsigned i = 0; i < argumentCount; ++i)
+ arguments[i] = objectToValue(_context, [argumentArray objectAtIndex:i]);
+
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSObjectRef result = JSObjectCallAsConstructor([_context JSGlobalContextRef], object, argumentCount, arguments, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ return [JSValue valueWithJSValueRef:result inContext:_context];
+}
+
+- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments
+{
+ NSUInteger argumentCount = [arguments count];
+ JSValueRef argumentArray[argumentCount];
+ for (unsigned i = 0; i < argumentCount; ++i)
+ argumentArray[i] = objectToValue(_context, [arguments objectAtIndex:i]);
+
+ JSValueRef exception = 0;
+ JSObjectRef thisObject = JSValueToObject([_context JSGlobalContextRef], m_value, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSStringRef name = JSStringCreateWithCFString((CFStringRef)method);
+ JSValueRef function = JSObjectGetProperty([_context JSGlobalContextRef], thisObject, name, &exception);
+ JSStringRelease(name);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSObjectRef object = JSValueToObject([_context JSGlobalContextRef], function, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ JSValueRef result = JSObjectCallAsFunction([_context JSGlobalContextRef], object, thisObject, argumentCount, argumentArray, &exception);
+ if (exception)
+ return [_context valueFromNotifyException:exception];
+
+ return [JSValue valueWithJSValueRef:result inContext:_context];
+}
+
+@end
+
+@implementation JSValue(StructSupport)
+
+- (CGPoint)toPoint
+{
+ return (CGPoint){
+ [self[@"x"] toDouble],
+ [self[@"y"] toDouble]
+ };
+}
+
+- (NSRange)toRange
+{
+ return (NSRange){
+ [[self[@"location"] toNumber] unsignedIntegerValue],
+ [[self[@"length"] toNumber] unsignedIntegerValue]
+ };
+}
+
+- (CGRect)toRect
+{
+ return (CGRect){
+ [self toPoint],
+ [self toSize]
+ };
+}
+
+- (CGSize)toSize
+{
+ return (CGSize){
+ [self[@"width"] toDouble],
+ [self[@"height"] toDouble]
+ };
+}
+
++ (JSValue *)valueWithPoint:(CGPoint)point inContext:(JSContext *)context
+{
+ return [JSValue valueWithObject:@{
+ @"x":@(point.x),
+ @"y":@(point.y)
+ } inContext:context];
+}
+
++ (JSValue *)valueWithRange:(NSRange)range inContext:(JSContext *)context
+{
+ return [JSValue valueWithObject:@{
+ @"location":@(range.location),
+ @"length":@(range.length)
+ } inContext:context];
+}
+
++ (JSValue *)valueWithRect:(CGRect)rect inContext:(JSContext *)context
+{
+ return [JSValue valueWithObject:@{
+ @"x":@(rect.origin.x),
+ @"y":@(rect.origin.y),
+ @"width":@(rect.size.width),
+ @"height":@(rect.size.height)
+ } inContext:context];
+}
+
++ (JSValue *)valueWithSize:(CGSize)size inContext:(JSContext *)context
+{
+ return [JSValue valueWithObject:@{
+ @"width":@(size.width),
+ @"height":@(size.height)
+ } inContext:context];
+}
+
+@end
+
+@implementation JSValue(SubscriptSupport)
+
+- (JSValue *)objectForKeyedSubscript:(id)key
+{
+ if (![key isKindOfClass:[NSString class]]) {
+ key = [[JSValue valueWithObject:key inContext:_context] toString];
+ if (!key)
+ return [JSValue valueWithUndefinedInContext:_context];
+ }
+
+ return [self valueForProperty:(NSString *)key];
+}
+
+- (JSValue *)objectAtIndexedSubscript:(NSUInteger)index
+{
+ return [self valueAtIndex:index];
+}
+
+- (void)setObject:(id)object forKeyedSubscript:(NSObject <NSCopying> *)key
+{
+ if (![key isKindOfClass:[NSString class]]) {
+ key = [[JSValue valueWithObject:key inContext:_context] toString];
+ if (!key)
+ return;
+ }
+
+ [self setValue:object forProperty:(NSString *)key];
+}
+
+- (void)setObject:(id)object atIndexedSubscript:(NSUInteger)index
+{
+ [self setValue:object atIndex:index];
+}
+
+@end
+
+inline bool isDate(JSObjectRef object, JSGlobalContextRef context)
+{
+ JSC::APIEntryShim entryShim(toJS(context));
+ return toJS(object)->inherits(&JSC::DateInstance::s_info);
+}
+
+inline bool isArray(JSObjectRef object, JSGlobalContextRef context)
+{
+ JSC::APIEntryShim entryShim(toJS(context));
+ return toJS(object)->inherits(&JSC::JSArray::s_info);
+}
+
+@implementation JSValue(Internal)
+
+enum ConversionType {
+ ContainerNone,
+ ContainerArray,
+ ContainerDictionary
+};
+
+class JSContainerConvertor {
+public:
+ struct Task {
+ JSValueRef js;
+ id objc;
+ ConversionType type;
+ };
+
+ JSContainerConvertor(JSGlobalContextRef context)
+ : m_context(context)
+ {
+ }
+
+ id convert(JSValueRef property);
+ void add(Task);
+ Task take();
+ bool isWorkListEmpty() const { return !m_worklist.size(); }
+
+private:
+ JSGlobalContextRef m_context;
+ HashMap<JSValueRef, id> m_objectMap;
+ Vector<Task> m_worklist;
+};
+
+inline id JSContainerConvertor::convert(JSValueRef value)
+{
+ HashMap<JSValueRef, id>::iterator iter = m_objectMap.find(value);
+ if (iter != m_objectMap.end())
+ return iter->value;
+
+ Task result = valueToObjectWithoutCopy(m_context, value);
+ if (result.js)
+ add(result);
+ return result.objc;
+}
+
+void JSContainerConvertor::add(Task task)
+{
+ m_objectMap.add(task.js, task.objc);
+ if (task.type != ContainerNone)
+ m_worklist.append(task);
+}
+
+JSContainerConvertor::Task JSContainerConvertor::take()
+{
+ ASSERT(!isWorkListEmpty());
+ Task last = m_worklist.last();
+ m_worklist.removeLast();
+ return last;
+}
+
+static JSContainerConvertor::Task valueToObjectWithoutCopy(JSGlobalContextRef context, JSValueRef value)
+{
+ if (!JSValueIsObject(context, value)) {
+ id primitive;
+ if (JSValueIsBoolean(context, value))
+ primitive = JSValueToBoolean(context, value) ? @YES : @NO;
+ else if (JSValueIsNumber(context, value)) {
+ // Normalize the number, so it will unique correctly in the hash map -
+ // it's nicer not to leak this internal implementation detail!
+ value = JSValueMakeNumber(context, JSValueToNumber(context, value, 0));
+ primitive = [NSNumber numberWithDouble:JSValueToNumber(context, value, 0)];
+ } else if (JSValueIsString(context, value)) {
+ // Would be nice to unique strings, too.
+ JSStringRef jsstring = JSValueToStringCopy(context, value, 0);
+ NSString * stringNS = (NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring);
+ JSStringRelease(jsstring);
+ primitive = [stringNS autorelease];
+ } else if (JSValueIsNull(context, value))
+ primitive = [NSNull null];
+ else {
+ ASSERT(JSValueIsUndefined(context, value));
+ primitive = nil;
+ }
+ return (JSContainerConvertor::Task){ value, primitive, ContainerNone };
+ }
+
+ JSObjectRef object = JSValueToObject(context, value, 0);
+
+ if (id wrapped = tryUnwrapObjcObject(context, object))
+ return (JSContainerConvertor::Task){ object, wrapped, ContainerNone };
+
+ if (isDate(object, context))
+ return (JSContainerConvertor::Task){ object, [NSDate dateWithTimeIntervalSince1970:JSValueToNumber(context, object, 0)], ContainerNone };
+
+ if (isArray(object, context))
+ return (JSContainerConvertor::Task){ object, [NSMutableArray array], ContainerArray };
+
+ return (JSContainerConvertor::Task){ object, [NSMutableDictionary dictionary], ContainerDictionary };
+}
+
+static id containerValueToObject(JSGlobalContextRef context, JSContainerConvertor::Task task)
+{
+ ASSERT(task.type != ContainerNone);
+ JSContainerConvertor convertor(context);
+ convertor.add(task);
+ ASSERT(!convertor.isWorkListEmpty());
+
+ do {
+ JSContainerConvertor::Task current = convertor.take();
+ ASSERT(JSValueIsObject(context, current.js));
+ JSObjectRef js = JSValueToObject(context, current.js, 0);
+
+ if (current.type == ContainerArray) {
+ ASSERT([current.objc isKindOfClass:[NSMutableArray class]]);
+ NSMutableArray *array = (NSMutableArray *)current.objc;
+
+ JSStringRef lengthString = JSStringCreateWithUTF8CString("length");
+ unsigned length = JSC::toUInt32(JSValueToNumber(context, JSObjectGetProperty(context, js, lengthString, 0), 0));
+ JSStringRelease(lengthString);
+
+ for (unsigned i = 0; i < length; ++i) {
+ id objc = convertor.convert(JSObjectGetPropertyAtIndex(context, js, i, 0));
+ [array addObject:objc ? objc : [NSNull null]];
+ }
+ } else {
+ ASSERT([current.objc isKindOfClass:[NSMutableDictionary class]]);
+ NSMutableDictionary *dictionary = (NSMutableDictionary *)current.objc;
+
+ JSPropertyNameArrayRef propertyNameArray = JSObjectCopyPropertyNames(context, js);
+ size_t length = JSPropertyNameArrayGetCount(propertyNameArray);
+
+ for (size_t i = 0; i < length; ++i) {
+ JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNameArray, i);
+ if (id objc = convertor.convert(JSObjectGetProperty(context, js, propertyName, 0)))
+ dictionary[[(NSString *)JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]] = objc;
+ }
+
+ JSPropertyNameArrayRelease(propertyNameArray);
+ }
+
+ } while (!convertor.isWorkListEmpty());
+
+ return task.objc;
+}
+
+id valueToObject(JSContext *context, JSValueRef value)
+{
+ JSContainerConvertor::Task result = valueToObjectWithoutCopy([context JSGlobalContextRef], value);
+ if (result.type == ContainerNone)
+ return result.objc;
+ return containerValueToObject([context JSGlobalContextRef], result);
+}
+
+id valueToNumber(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
+{
+ ASSERT(!*exception);
+ if (id wrapped = tryUnwrapObjcObject(context, value)) {
+ if ([wrapped isKindOfClass:[NSNumber class]])
+ return wrapped;
+ }
+
+ if (JSValueIsBoolean(context, value))
+ return JSValueToBoolean(context, value) ? @YES : @NO;
+
+ double result = JSValueToNumber(context, value, exception);
+ return [NSNumber numberWithDouble:*exception ? std::numeric_limits<double>::quiet_NaN() : result];
+}
+
+id valueToString(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
+{
+ ASSERT(!*exception);
+ if (id wrapped = tryUnwrapObjcObject(context, value)) {
+ if ([wrapped isKindOfClass:[NSString class]])
+ return wrapped;
+ }
+
+ JSStringRef jsstring = JSValueToStringCopy(context, value, exception);
+ if (*exception) {
+ ASSERT(!jsstring);
+ return nil;
+ }
+
+ NSString *stringNS = [(NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsstring) autorelease];
+ JSStringRelease(jsstring);
+ return stringNS;
+}
+
+id valueToDate(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
+{
+ ASSERT(!*exception);
+ if (id wrapped = tryUnwrapObjcObject(context, value)) {
+ if ([wrapped isKindOfClass:[NSDate class]])
+ return wrapped;
+ }
+
+ double result = JSValueToNumber(context, value, exception);
+ return *exception ? nil : [NSDate dateWithTimeIntervalSince1970:result];
+}
+
+id valueToArray(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
+{
+ ASSERT(!*exception);
+ if (id wrapped = tryUnwrapObjcObject(context, value)) {
+ if ([wrapped isKindOfClass:[NSArray class]])
+ return wrapped;
+ }
+
+ if (JSValueIsObject(context, value))
+ return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableArray array], ContainerArray});
+
+ if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
+ *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSArray"));
+ return nil;
+}
+
+id valueToDictionary(JSGlobalContextRef context, JSValueRef value, JSValueRef* exception)
+{
+ ASSERT(!*exception);
+ if (id wrapped = tryUnwrapObjcObject(context, value)) {
+ if ([wrapped isKindOfClass:[NSDictionary class]])
+ return wrapped;
+ }
+
+ if (JSValueIsObject(context, value))
+ return containerValueToObject(context, (JSContainerConvertor::Task){ value, [NSMutableDictionary dictionary], ContainerDictionary});
+
+ if (!(JSValueIsNull(context, value) || JSValueIsUndefined(context, value)))
+ *exception = toRef(JSC::createTypeError(toJS(context), "Cannot convert primitive to NSDictionary"));
+ return nil;
+}
+
+class ObjcContainerConvertor {
+public:
+ struct Task {
+ id objc;
+ JSValueRef js;
+ ConversionType type;
+ };
+
+ ObjcContainerConvertor(JSContext *context)
+ : m_context(context)
+ {
+ }
+
+ JSValueRef convert(id object);
+ void add(Task);
+ Task take();
+ bool isWorkListEmpty() const { return !m_worklist.size(); }
+
+private:
+ JSContext *m_context;
+ HashMap<id, JSValueRef> m_objectMap;
+ Vector<Task> m_worklist;
+};
+
+JSValueRef ObjcContainerConvertor::convert(id object)
+{
+ ASSERT(object);
+
+ auto it = m_objectMap.find(object);
+ if (it != m_objectMap.end())
+ return it->value;
+
+ ObjcContainerConvertor::Task task = objectToValueWithoutCopy(m_context, object);
+ add(task);
+ return task.js;
+}
+
+void ObjcContainerConvertor::add(ObjcContainerConvertor::Task task)
+{
+ m_objectMap.add(task.objc, task.js);
+ if (task.type != ContainerNone)
+ m_worklist.append(task);
+}
+
+ObjcContainerConvertor::Task ObjcContainerConvertor::take()
+{
+ ASSERT(!isWorkListEmpty());
+ Task last = m_worklist.last();
+ m_worklist.removeLast();
+ return last;
+}
+
+inline bool isNSBoolean(id object)
+{
+ ASSERT([@YES class] == [@NO class]);
+ ASSERT([@YES class] != [NSNumber class]);
+ ASSERT([[@YES class] isSubclassOfClass:[NSNumber class]]);
+ return [object isKindOfClass:[@YES class]];
+}
+
+static ObjcContainerConvertor::Task objectToValueWithoutCopy(JSContext *context, id object)
+{
+ JSGlobalContextRef contextRef = [context JSGlobalContextRef];
+
+ if (!object)
+ return (ObjcContainerConvertor::Task){ object, JSValueMakeUndefined(contextRef), ContainerNone };
+
+ if (!class_conformsToProtocol(object_getClass(object), getJSExportProtocol())) {
+ if ([object isKindOfClass:[NSArray class]])
+ return (ObjcContainerConvertor::Task){ object, JSObjectMakeArray(contextRef, 0, NULL, 0), ContainerArray };
+
+ if ([object isKindOfClass:[NSDictionary class]])
+ return (ObjcContainerConvertor::Task){ object, JSObjectMake(contextRef, 0, 0), ContainerDictionary };
+
+ if ([object isKindOfClass:[NSNull class]])
+ return (ObjcContainerConvertor::Task){ object, JSValueMakeNull(contextRef), ContainerNone };
+
+ if ([object isKindOfClass:[JSValue class]])
+ return (ObjcContainerConvertor::Task){ object, ((JSValue *)object)->m_value, ContainerNone };
+
+ if ([object isKindOfClass:[NSString class]]) {
+ JSStringRef string = JSStringCreateWithCFString((CFStringRef)object);
+ JSValueRef js = JSValueMakeString(contextRef, string);
+ JSStringRelease(string);
+ return (ObjcContainerConvertor::Task){ object, js, ContainerNone };
+ }
+
+ if ([object isKindOfClass:[NSNumber class]]) {
+ if (isNSBoolean(object))
+ return (ObjcContainerConvertor::Task){ object, JSValueMakeBoolean(contextRef, [object boolValue]), ContainerNone };
+ return (ObjcContainerConvertor::Task){ object, JSValueMakeNumber(contextRef, [object doubleValue]), ContainerNone };
+ }
+
+ if ([object isKindOfClass:[NSDate class]]) {
+ JSValueRef argument = JSValueMakeNumber(contextRef, [object timeIntervalSince1970]);
+ JSObjectRef result = JSObjectMakeDate(contextRef, 1, &argument, 0);
+ return (ObjcContainerConvertor::Task){ object, result, ContainerNone };
+ }
+
+ if ([object isKindOfClass:[JSManagedValue class]]) {
+ JSValue *value = [static_cast<JSManagedValue *>(object) value];
+ if (!value)
+ return (ObjcContainerConvertor::Task) { object, JSValueMakeUndefined(contextRef), ContainerNone };
+ return (ObjcContainerConvertor::Task){ object, value->m_value, ContainerNone };
+ }
+ }
+
+ return (ObjcContainerConvertor::Task){ object, valueInternalValue([context wrapperForObjCObject:object]), ContainerNone };
+}
+
+JSValueRef objectToValue(JSContext *context, id object)
+{
+ JSGlobalContextRef contextRef = [context JSGlobalContextRef];
+
+ ObjcContainerConvertor::Task task = objectToValueWithoutCopy(context, object);
+ if (task.type == ContainerNone)
+ return task.js;
+
+ ObjcContainerConvertor convertor(context);
+ convertor.add(task);
+ ASSERT(!convertor.isWorkListEmpty());
+
+ do {
+ ObjcContainerConvertor::Task current = convertor.take();
+ ASSERT(JSValueIsObject(contextRef, current.js));
+ JSObjectRef js = JSValueToObject(contextRef, current.js, 0);
+
+ if (current.type == ContainerArray) {
+ ASSERT([current.objc isKindOfClass:[NSArray class]]);
+ NSArray *array = (NSArray *)current.objc;
+ NSUInteger count = [array count];
+ for (NSUInteger index = 0; index < count; ++index)
+ JSObjectSetPropertyAtIndex(contextRef, js, index, convertor.convert([array objectAtIndex:index]), 0);
+ } else {
+ ASSERT(current.type == ContainerDictionary);
+ ASSERT([current.objc isKindOfClass:[NSDictionary class]]);
+ NSDictionary *dictionary = (NSDictionary *)current.objc;
+ for (id key in [dictionary keyEnumerator]) {
+ if ([key isKindOfClass:[NSString class]]) {
+ JSStringRef propertyName = JSStringCreateWithCFString((CFStringRef)key);
+ JSObjectSetProperty(contextRef, js, propertyName, convertor.convert([dictionary objectForKey:key]), 0, 0);
+ JSStringRelease(propertyName);
+ }
+ }
+ }
+
+ } while (!convertor.isWorkListEmpty());
+
+ return task.js;
+}
+
+JSValueRef valueInternalValue(JSValue * value)
+{
+ return value->m_value;
+}
+
++ (JSValue *)valueWithJSValueRef:(JSValueRef)value inContext:(JSContext *)context
+{
+ return [context wrapperForJSObject:value];
+}
+
+- (JSValue *)init
+{
+ return nil;
+}
+
+- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context
+{
+ if (!value || !context)
+ return nil;
+
+ self = [super init];
+ if (!self)
+ return nil;
+
+ _context = [context retain];
+ m_value = value;
+ JSValueProtect([_context JSGlobalContextRef], m_value);
+ return self;
+}
+
+struct StructTagHandler {
+ SEL typeToValueSEL;
+ SEL valueToTypeSEL;
+};
+typedef HashMap<String, StructTagHandler> StructHandlers;
+
+static StructHandlers* createStructHandlerMap()
+{
+ StructHandlers* structHandlers = new StructHandlers();
+
+ size_t valueWithXinContextLength = strlen("valueWithX:inContext:");
+ size_t toXLength = strlen("toX");
+
+ // Step 1: find all valueWith<Foo>:inContext: class methods in JSValue.
+ forEachMethodInClass(object_getClass([JSValue class]), ^(Method method){
+ SEL selector = method_getName(method);
+ const char* name = sel_getName(selector);
+ size_t nameLength = strlen(name);
+ // Check for valueWith<Foo>:context:
+ if (nameLength < valueWithXinContextLength || memcmp(name, "valueWith", 9) || memcmp(name + nameLength - 11, ":inContext:", 11))
+ return;
+ // Check for [ id, SEL, <type>, <contextType> ]
+ if (method_getNumberOfArguments(method) != 4)
+ return;
+ char idType[3];
+ // Check 2nd argument type is "@"
+ char* secondType = method_copyArgumentType(method, 3);
+ if (strcmp(secondType, "@") != 0) {
+ free(secondType);
+ return;
+ }
+ free(secondType);
+ // Check result type is also "@"
+ method_getReturnType(method, idType, 3);
+ if (strcmp(idType, "@") != 0)
+ return;
+ char* type = method_copyArgumentType(method, 2);
+ structHandlers->add(StringImpl::create(type), (StructTagHandler){ selector, 0 });
+ free(type);
+ });
+
+ // Step 2: find all to<Foo> instance methods in JSValue.
+ forEachMethodInClass([JSValue class], ^(Method method){
+ SEL selector = method_getName(method);
+ const char* name = sel_getName(selector);
+ size_t nameLength = strlen(name);
+ // Check for to<Foo>
+ if (nameLength < toXLength || memcmp(name, "to", 2))
+ return;
+ // Check for [ id, SEL ]
+ if (method_getNumberOfArguments(method) != 2)
+ return;
+ // Try to find a matching valueWith<Foo>:context: method.
+ char* type = method_copyReturnType(method);
+
+ StructHandlers::iterator iter = structHandlers->find(type);
+ free(type);
+ if (iter == structHandlers->end())
+ return;
+ StructTagHandler& handler = iter->value;
+
+ // check that strlen(<foo>) == strlen(<Foo>)
+ const char* valueWithName = sel_getName(handler.typeToValueSEL);
+ size_t valueWithLength = strlen(valueWithName);
+ if (valueWithLength - valueWithXinContextLength != nameLength - toXLength)
+ return;
+ // Check that <Foo> == <Foo>
+ if (memcmp(valueWithName + 9, name + 2, nameLength - toXLength - 1))
+ return;
+ handler.valueToTypeSEL = selector;
+ });
+
+ // Step 3: clean up - remove entries where we found prospective valueWith<Foo>:inContext: conversions, but no matching to<Foo> methods.
+ typedef HashSet<String> RemoveSet;
+ RemoveSet removeSet;
+ for (StructHandlers::iterator iter = structHandlers->begin(); iter != structHandlers->end(); ++iter) {
+ StructTagHandler& handler = iter->value;
+ if (!handler.valueToTypeSEL)
+ removeSet.add(iter->key);
+ }
+
+ for (RemoveSet::iterator iter = removeSet.begin(); iter != removeSet.end(); ++iter)
+ structHandlers->remove(*iter);
+
+ return structHandlers;
+}
+
+static StructTagHandler* handerForStructTag(const char* encodedType)
+{
+ static SpinLock handerForStructTagLock = SPINLOCK_INITIALIZER;
+ SpinLockHolder lockHolder(&handerForStructTagLock);
+
+ static StructHandlers* structHandlers = createStructHandlerMap();
+
+ StructHandlers::iterator iter = structHandlers->find(encodedType);
+ if (iter == structHandlers->end())
+ return 0;
+ return &iter->value;
+}
+
++ (SEL)selectorForStructToValue:(const char *)structTag
+{
+ StructTagHandler* handler = handerForStructTag(structTag);
+ return handler ? handler->typeToValueSEL : nil;
+}
+
++ (SEL)selectorForValueToStruct:(const char *)structTag
+{
+ StructTagHandler* handler = handerForStructTag(structTag);
+ return handler ? handler->valueToTypeSEL : nil;
+}
+
+- (void)dealloc
+{
+ JSValueUnprotect([_context JSGlobalContextRef], m_value);
+ [_context release];
+ _context = nil;
+ [super dealloc];
+}
+
+- (NSString *)description
+{
+ if (id wrapped = tryUnwrapObjcObject([_context JSGlobalContextRef], m_value))
+ return [wrapped description];
+ return [self toString];
+}
+
+NSInvocation *typeToValueInvocationFor(const char* encodedType)
+{
+ SEL selector = [JSValue selectorForStructToValue:encodedType];
+ if (!selector)
+ return 0;
+
+ const char* methodTypes = method_getTypeEncoding(class_getClassMethod([JSValue class], selector));
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
+ [invocation setSelector:selector];
+ return invocation;
+}
+
+NSInvocation *valueToTypeInvocationFor(const char* encodedType)
+{
+ SEL selector = [JSValue selectorForValueToStruct:encodedType];
+ if (!selector)
+ return 0;
+
+ const char* methodTypes = method_getTypeEncoding(class_getInstanceMethod([JSValue class], selector));
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:methodTypes]];
+ [invocation setSelector:selector];
+ return invocation;
+}
+
+@end
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSValueInternal.h b/Source/JavaScriptCore/API/JSValueInternal.h
new file mode 100644
index 000000000..4f1a8f69c
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSValueInternal.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSValueInternal_h
+#define JSValueInternal_h
+
+#import <JavaScriptCore/JavaScriptCore.h>
+#import <JavaScriptCore/JSValue.h>
+
+#if JSC_OBJC_API_ENABLED
+
+@interface JSValue(Internal)
+
+JSValueRef valueInternalValue(JSValue *);
+
+- (JSValue *)initWithValue:(JSValueRef)value inContext:(JSContext *)context;
+
+JSValueRef objectToValue(JSContext *, id);
+id valueToObject(JSContext *, JSValueRef);
+id valueToNumber(JSGlobalContextRef, JSValueRef, JSValueRef* exception);
+id valueToString(JSGlobalContextRef, JSValueRef, JSValueRef* exception);
+id valueToDate(JSGlobalContextRef, JSValueRef, JSValueRef* exception);
+id valueToArray(JSGlobalContextRef, JSValueRef, JSValueRef* exception);
+id valueToDictionary(JSGlobalContextRef, JSValueRef, JSValueRef* exception);
+
++ (SEL)selectorForStructToValue:(const char *)structTag;
++ (SEL)selectorForValueToStruct:(const char *)structTag;
+
+@end
+
+NSInvocation *typeToValueInvocationFor(const char* encodedType);
+NSInvocation *valueToTypeInvocationFor(const char* encodedType);
+
+#endif
+
+#endif // JSValueInternal_h
diff --git a/Source/JavaScriptCore/API/JSValueRef.cpp b/Source/JavaScriptCore/API/JSValueRef.cpp
index 5ff7c03c6..81a2db7b7 100644
--- a/Source/JavaScriptCore/API/JSValueRef.cpp
+++ b/Source/JavaScriptCore/API/JSValueRef.cpp
@@ -28,15 +28,16 @@
#include "APICast.h"
#include "APIShims.h"
+#include "JSAPIWrapperObject.h"
#include "JSCallbackObject.h"
+#include <runtime/JSCJSValue.h>
#include <runtime/JSGlobalObject.h>
#include <runtime/JSONObject.h>
#include <runtime/JSString.h>
#include <runtime/LiteralParser.h>
#include <runtime/Operations.h>
#include <runtime/Protect.h>
-#include <runtime/JSValue.h>
#include <wtf/Assertions.h>
#include <wtf/text/StringHash.h>
@@ -44,10 +45,29 @@
#include <algorithm> // for std::min
+#if PLATFORM(MAC)
+#include <mach-o/dyld.h>
+#endif
+
using namespace JSC;
+#if PLATFORM(MAC)
+static bool evernoteHackNeeded()
+{
+ static const int32_t webkitLastVersionWithEvernoteHack = 35133959;
+ static bool hackNeeded = CFEqual(CFBundleGetIdentifier(CFBundleGetMainBundle()), CFSTR("com.evernote.Evernote"))
+ && NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitLastVersionWithEvernoteHack;
+
+ return hackNeeded;
+}
+#endif
+
::JSType JSValueGetType(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return kJSTypeUndefined;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -69,6 +89,10 @@ using namespace JSC;
bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -78,6 +102,10 @@ bool JSValueIsUndefined(JSContextRef ctx, JSValueRef value)
bool JSValueIsNull(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -87,6 +115,10 @@ bool JSValueIsNull(JSContextRef ctx, JSValueRef value)
bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -96,6 +128,10 @@ bool JSValueIsBoolean(JSContextRef ctx, JSValueRef value)
bool JSValueIsNumber(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -105,6 +141,10 @@ bool JSValueIsNumber(JSContextRef ctx, JSValueRef value)
bool JSValueIsString(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -114,6 +154,10 @@ bool JSValueIsString(JSContextRef ctx, JSValueRef value)
bool JSValueIsObject(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -123,6 +167,10 @@ bool JSValueIsObject(JSContextRef ctx, JSValueRef value)
bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsClass)
{
+ if (!ctx || !jsClass) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -133,12 +181,20 @@ bool JSValueIsObjectOfClass(JSContextRef ctx, JSValueRef value, JSClassRef jsCla
return jsCast<JSCallbackObject<JSGlobalObject>*>(o)->inherits(jsClass);
if (o->inherits(&JSCallbackObject<JSDestructibleObject>::s_info))
return jsCast<JSCallbackObject<JSDestructibleObject>*>(o)->inherits(jsClass);
+#if JSC_OBJC_API_ENABLED
+ if (o->inherits(&JSCallbackObject<JSAPIWrapperObject>::s_info))
+ return jsCast<JSCallbackObject<JSAPIWrapperObject>*>(o)->inherits(jsClass);
+#endif
}
return false;
}
bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -156,6 +212,10 @@ bool JSValueIsEqual(JSContextRef ctx, JSValueRef a, JSValueRef b, JSValueRef* ex
bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -167,6 +227,10 @@ bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b)
bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -186,6 +250,10 @@ bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObject
JSValueRef JSValueMakeUndefined(JSContextRef ctx)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -194,6 +262,10 @@ JSValueRef JSValueMakeUndefined(JSContextRef ctx)
JSValueRef JSValueMakeNull(JSContextRef ctx)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -202,6 +274,10 @@ JSValueRef JSValueMakeNull(JSContextRef ctx)
JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -210,13 +286,17 @@ JSValueRef JSValueMakeBoolean(JSContextRef ctx, bool value)
JSValueRef JSValueMakeNumber(JSContextRef ctx, double value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
// Our JSValue representation relies on a standard bit pattern for NaN. NaNs
// generated internally to JavaScriptCore naturally have that representation,
// but an external NaN might not.
- if (isnan(value))
+ if (std::isnan(value))
value = QNaN;
return toRef(exec, jsNumber(value));
@@ -224,6 +304,10 @@ JSValueRef JSValueMakeNumber(JSContextRef ctx, double value)
JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -232,6 +316,10 @@ JSValueRef JSValueMakeString(JSContextRef ctx, JSStringRef string)
JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
String str = string->string();
@@ -246,6 +334,10 @@ JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef apiValue, unsigned indent, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSValue value = toJS(exec, apiValue);
@@ -263,6 +355,10 @@ JSStringRef JSValueCreateJSONString(JSContextRef ctx, JSValueRef apiValue, unsig
bool JSValueToBoolean(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -272,6 +368,10 @@ bool JSValueToBoolean(JSContextRef ctx, JSValueRef value)
double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return QNaN;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -289,6 +389,10 @@ double JSValueToNumber(JSContextRef ctx, JSValueRef value, JSValueRef* exception
JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -306,6 +410,10 @@ JSStringRef JSValueToStringCopy(JSContextRef ctx, JSValueRef value, JSValueRef*
JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exception)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -323,6 +431,10 @@ JSObjectRef JSValueToObject(JSContextRef ctx, JSValueRef value, JSValueRef* exce
void JSValueProtect(JSContextRef ctx, JSValueRef value)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
@@ -332,6 +444,11 @@ void JSValueProtect(JSContextRef ctx, JSValueRef value)
void JSValueUnprotect(JSContextRef ctx, JSValueRef value)
{
+#if PLATFORM(MAC)
+ if ((!value || !ctx) && evernoteHackNeeded())
+ return;
+#endif
+
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
diff --git a/Source/JavaScriptCore/API/JSVirtualMachine.h b/Source/JavaScriptCore/API/JSVirtualMachine.h
new file mode 100644
index 000000000..b50616f4c
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSVirtualMachine.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <JavaScriptCore/JavaScriptCore.h>
+
+#if JSC_OBJC_API_ENABLED
+
+// An instance of JSVirtualMachine represents a single JavaScript "object space"
+// or set of execution resources. Thread safety is supported by locking the
+// virtual machine, with concurrent JavaScript execution supported by allocating
+// separate instances of JSVirtualMachine.
+
+NS_CLASS_AVAILABLE(10_9, NA)
+@interface JSVirtualMachine : NSObject
+
+// Create a new JSVirtualMachine.
+- (id)init;
+
+// addManagedReference:withOwner and removeManagedReference:withOwner allow
+// clients of JSVirtualMachine to make the JavaScript runtime aware of
+// arbitrary external Objective-C object graphs. The runtime can then use
+// this information to retain any JavaScript values that are referenced
+// from somewhere in said object graph.
+//
+// For correct behavior clients must make their external object graphs
+// reachable from within the JavaScript runtime. If an Objective-C object is
+// reachable from within the JavaScript runtime, all managed references
+// transitively reachable from it as recorded with
+// addManagedReference:withOwner: will be scanned by the garbage collector.
+//
+- (void)addManagedReference:(id)object withOwner:(id)owner;
+- (void)removeManagedReference:(id)object withOwner:(id)owner;
+
+@end
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSVirtualMachine.mm b/Source/JavaScriptCore/API/JSVirtualMachine.mm
new file mode 100644
index 000000000..6bada34a6
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSVirtualMachine.mm
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#import "JavaScriptCore.h"
+
+#if JSC_OBJC_API_ENABLED
+
+#import "APICast.h"
+#import "APIShims.h"
+#import "JSVirtualMachine.h"
+#import "JSVirtualMachineInternal.h"
+#import "JSWrapperMap.h"
+
+static NSMapTable *globalWrapperCache = 0;
+
+static Mutex& wrapperCacheLock()
+{
+ DEFINE_STATIC_LOCAL(Mutex, mutex, ());
+ return mutex;
+}
+
+static void initWrapperCache()
+{
+ ASSERT(!globalWrapperCache);
+ NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
+ NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+ globalWrapperCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
+}
+
+static NSMapTable *wrapperCache()
+{
+ if (!globalWrapperCache)
+ initWrapperCache();
+ return globalWrapperCache;
+}
+
+@interface JSVMWrapperCache : NSObject
++ (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group;
++ (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group;
+@end
+
+@implementation JSVMWrapperCache
+
++ (void)addWrapper:(JSVirtualMachine *)wrapper forJSContextGroupRef:(JSContextGroupRef)group
+{
+ MutexLocker locker(wrapperCacheLock());
+ NSMapInsert(wrapperCache(), group, wrapper);
+}
+
++ (JSVirtualMachine *)wrapperForJSContextGroupRef:(JSContextGroupRef)group
+{
+ MutexLocker locker(wrapperCacheLock());
+ return static_cast<JSVirtualMachine *>(NSMapGet(wrapperCache(), group));
+}
+
+@end
+
+@implementation JSVirtualMachine {
+ JSContextGroupRef m_group;
+ NSMapTable *m_contextCache;
+ NSMapTable *m_externalObjectGraph;
+}
+
+- (id)init
+{
+ JSContextGroupRef group = JSContextGroupCreate();
+ self = [self initWithContextGroupRef:group];
+ // The extra JSContextGroupRetain is balanced here.
+ JSContextGroupRelease(group);
+ return self;
+}
+
+- (id)initWithContextGroupRef:(JSContextGroupRef)group
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ m_group = JSContextGroupRetain(group);
+
+ NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
+ NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+ m_contextCache = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
+
+ NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+ NSPointerFunctionsOptions strongIDOptions = NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality;
+ m_externalObjectGraph = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:strongIDOptions capacity:0];
+
+ [JSVMWrapperCache addWrapper:self forJSContextGroupRef:group];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ JSContextGroupRelease(m_group);
+ [m_contextCache release];
+ [m_externalObjectGraph release];
+ [super dealloc];
+}
+
+static id getInternalObjcObject(id object)
+{
+ if ([object isKindOfClass:[JSManagedValue class]]) {
+ JSValue* value = [static_cast<JSManagedValue *>(object) value];
+ id temp = tryUnwrapObjcObject([value.context JSGlobalContextRef], [value JSValueRef]);
+ if (temp)
+ return temp;
+ return object;
+ }
+
+ if ([object isKindOfClass:[JSValue class]]) {
+ JSValue *value = static_cast<JSValue *>(object);
+ object = tryUnwrapObjcObject([value.context JSGlobalContextRef], [value JSValueRef]);
+ }
+
+ return object;
+}
+
+- (void)addManagedReference:(id)object withOwner:(id)owner
+{
+ object = getInternalObjcObject(object);
+ owner = getInternalObjcObject(owner);
+
+ if (!object || !owner)
+ return;
+
+ JSC::APIEntryShim shim(toJS(m_group));
+
+ NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner];
+ if (!ownedObjects) {
+ NSPointerFunctionsOptions weakIDOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+ NSPointerFunctionsOptions integerOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsIntegerPersonality;
+ ownedObjects = [[NSMapTable alloc] initWithKeyOptions:weakIDOptions valueOptions:integerOptions capacity:1];
+
+ [m_externalObjectGraph setObject:ownedObjects forKey:owner];
+ [ownedObjects release];
+ }
+ NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(reinterpret_cast<size_t>(NSMapGet(ownedObjects, object)) + 1));
+}
+
+- (void)removeManagedReference:(id)object withOwner:(id)owner
+{
+ object = getInternalObjcObject(object);
+ owner = getInternalObjcObject(owner);
+
+ if (!object || !owner)
+ return;
+
+ JSC::APIEntryShim shim(toJS(m_group));
+
+ NSMapTable *ownedObjects = [m_externalObjectGraph objectForKey:owner];
+ if (!ownedObjects)
+ return;
+
+ size_t count = reinterpret_cast<size_t>(NSMapGet(ownedObjects, object));
+ if (count > 1) {
+ NSMapInsert(ownedObjects, object, reinterpret_cast<void*>(count - 1));
+ return;
+ }
+
+ if (count == 1)
+ NSMapRemove(ownedObjects, object);
+
+ if (![ownedObjects count])
+ [m_externalObjectGraph removeObjectForKey:owner];
+}
+
+@end
+
+@implementation JSVirtualMachine(Internal)
+
+JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *virtualMachine)
+{
+ return virtualMachine->m_group;
+}
+
++ (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group
+{
+ JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:group];
+ if (!virtualMachine)
+ virtualMachine = [[[JSVirtualMachine alloc] initWithContextGroupRef:group] autorelease];
+ return virtualMachine;
+}
+
+- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext
+{
+ return static_cast<JSContext *>(NSMapGet(m_contextCache, globalContext));
+}
+
+- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext
+{
+ NSMapInsert(m_contextCache, globalContext, wrapper);
+}
+
+- (NSMapTable *)externalObjectGraph
+{
+ return m_externalObjectGraph;
+}
+
+@end
+
+void scanExternalObjectGraph(JSC::VM& vm, JSC::SlotVisitor& visitor, void* root)
+{
+ @autoreleasepool {
+ JSVirtualMachine *virtualMachine = [JSVMWrapperCache wrapperForJSContextGroupRef:toRef(&vm)];
+ if (!virtualMachine)
+ return;
+ NSMapTable *externalObjectGraph = [virtualMachine externalObjectGraph];
+ Vector<void*> stack;
+ stack.append(root);
+ while (!stack.isEmpty()) {
+ void* nextRoot = stack.last();
+ stack.removeLast();
+ if (visitor.containsOpaqueRootTriState(nextRoot) == TrueTriState)
+ continue;
+ visitor.addOpaqueRoot(nextRoot);
+
+ NSMapTable *ownedObjects = [externalObjectGraph objectForKey:static_cast<id>(nextRoot)];
+ id ownedObject;
+ NSEnumerator *enumerator = [ownedObjects keyEnumerator];
+ while ((ownedObject = [enumerator nextObject])) {
+ ASSERT(reinterpret_cast<size_t>(NSMapGet(ownedObjects, ownedObject)) == 1);
+ stack.append(static_cast<void*>(ownedObject));
+ }
+ }
+ }
+}
+
+#endif
+
diff --git a/Source/JavaScriptCore/API/JSVirtualMachineInternal.h b/Source/JavaScriptCore/API/JSVirtualMachineInternal.h
new file mode 100644
index 000000000..729226566
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSVirtualMachineInternal.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef JSVirtualMachineInternal_h
+#define JSVirtualMachineInternal_h
+
+#import <JavaScriptCore/JavaScriptCore.h>
+
+#if JSC_OBJC_API_ENABLED
+
+namespace JSC {
+class VM;
+class SlotVisitor;
+}
+
+#if defined(__OBJC__)
+@interface JSVirtualMachine(Internal)
+
+JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *);
+
++ (JSVirtualMachine *)virtualMachineWithContextGroupRef:(JSContextGroupRef)group;
+
+- (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext;
+- (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext;
+
+- (NSMapTable *)externalObjectGraph;
+
+@end
+#endif // defined(__OBJC__)
+
+void scanExternalObjectGraph(JSC::VM&, JSC::SlotVisitor&, void* root);
+
+#endif
+
+#endif // JSVirtualMachineInternal_h
diff --git a/Source/JavaScriptCore/API/JSWeakObjectMapRefPrivate.cpp b/Source/JavaScriptCore/API/JSWeakObjectMapRefPrivate.cpp
index bdfce08ee..8cbe263a7 100644
--- a/Source/JavaScriptCore/API/JSWeakObjectMapRefPrivate.cpp
+++ b/Source/JavaScriptCore/API/JSWeakObjectMapRefPrivate.cpp
@@ -28,9 +28,11 @@
#include "APICast.h"
#include "APIShims.h"
+#include "JSCJSValue.h"
#include "JSCallbackObject.h"
-#include "JSValue.h"
#include "JSWeakObjectMapRefInternal.h"
+#include "Operations.h"
+#include "Weak.h"
#include <wtf/HashMap.h>
#include <wtf/text/StringHash.h>
@@ -52,17 +54,25 @@ JSWeakObjectMapRef JSWeakObjectMapCreate(JSContextRef context, void* privateData
void JSWeakObjectMapSet(JSContextRef ctx, JSWeakObjectMapRef map, void* key, JSObjectRef object)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
JSObject* obj = toJS(object);
if (!obj)
return;
ASSERT(obj->inherits(&JSCallbackObject<JSGlobalObject>::s_info) || obj->inherits(&JSCallbackObject<JSDestructibleObject>::s_info));
- map->map().set(exec->globalData(), key, obj);
+ map->map().set(key, obj);
}
JSObjectRef JSWeakObjectMapGet(JSContextRef ctx, JSWeakObjectMapRef map, void* key)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
return toRef(jsCast<JSObject*>(map->map().get(key)));
@@ -70,6 +80,10 @@ JSObjectRef JSWeakObjectMapGet(JSContextRef ctx, JSWeakObjectMapRef map, void* k
void JSWeakObjectMapRemove(JSContextRef ctx, JSWeakObjectMapRef map, void* key)
{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
ExecState* exec = toJS(ctx);
APIEntryShim entryShim(exec);
map->map().remove(key);
diff --git a/Source/JavaScriptCore/API/JSWrapperMap.h b/Source/JavaScriptCore/API/JSWrapperMap.h
new file mode 100644
index 000000000..ce74a9c61
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSWrapperMap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <JavaScriptCore/JavaScriptCore.h>
+#import <JSValueInternal.h>
+#import <objc/objc-runtime.h>
+
+#if JSC_OBJC_API_ENABLED
+
+@interface JSWrapperMap : NSObject
+
+- (id)initWithContext:(JSContext *)context;
+
+- (JSValue *)jsWrapperForObject:(id)object;
+
+- (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value;
+
+@end
+
+id tryUnwrapObjcObject(JSGlobalContextRef, JSValueRef);
+
+Protocol *getJSExportProtocol();
+Class getNSBlockClass();
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSWrapperMap.mm b/Source/JavaScriptCore/API/JSWrapperMap.mm
new file mode 100644
index 000000000..4dde1a659
--- /dev/null
+++ b/Source/JavaScriptCore/API/JSWrapperMap.mm
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#import "JavaScriptCore.h"
+
+#if JSC_OBJC_API_ENABLED
+
+#import "APICast.h"
+#import "APIShims.h"
+#import "JSAPIWrapperObject.h"
+#import "JSCallbackObject.h"
+#import "JSContextInternal.h"
+#import "JSWrapperMap.h"
+#import "ObjCCallbackFunction.h"
+#import "ObjcRuntimeExtras.h"
+#import "Operations.h"
+#import "WeakGCMap.h"
+#import <wtf/TCSpinLock.h>
+#import <wtf/Vector.h>
+
+@class JSObjCClassInfo;
+
+@interface JSWrapperMap ()
+
+- (JSObjCClassInfo*)classInfoForClass:(Class)cls;
+
+@end
+
+// Default conversion of selectors to property names.
+// All semicolons are removed, lowercase letters following a semicolon are capitalized.
+static NSString *selectorToPropertyName(const char* start)
+{
+ // Use 'index' to check for colons, if there are none, this is easy!
+ const char* firstColon = index(start, ':');
+ if (!firstColon)
+ return [NSString stringWithUTF8String:start];
+
+ // 'header' is the length of string up to the first colon.
+ size_t header = firstColon - start;
+ // The new string needs to be long enough to hold 'header', plus the remainder of the string, excluding
+ // at least one ':', but including a '\0'. (This is conservative if there are more than one ':').
+ char* buffer = static_cast<char*>(malloc(header + strlen(firstColon + 1) + 1));
+ // Copy 'header' characters, set output to point to the end of this & input to point past the first ':'.
+ memcpy(buffer, start, header);
+ char* output = buffer + header;
+ const char* input = start + header + 1;
+
+ // On entry to the loop, we have already skipped over a ':' from the input.
+ while (true) {
+ char c;
+ // Skip over any additional ':'s. We'll leave c holding the next character after the
+ // last ':', and input pointing past c.
+ while ((c = *(input++)) == ':');
+ // Copy the character, converting to upper case if necessary.
+ // If the character we copy is '\0', then we're done!
+ if (!(*(output++) = toupper(c)))
+ goto done;
+ // Loop over characters other than ':'.
+ while ((c = *(input++)) != ':') {
+ // Copy the character.
+ // If the character we copy is '\0', then we're done!
+ if (!(*(output++) = c))
+ goto done;
+ }
+ // If we get here, we've consumed a ':' - wash, rinse, repeat.
+ }
+done:
+ NSString *result = [NSString stringWithUTF8String:buffer];
+ free(buffer);
+ return result;
+}
+
+static JSObjectRef makeWrapper(JSContextRef ctx, JSClassRef jsClass, id wrappedObject)
+{
+ JSC::ExecState* exec = toJS(ctx);
+ JSC::APIEntryShim entryShim(exec);
+
+ ASSERT(jsClass);
+ JSC::JSCallbackObject<JSC::JSAPIWrapperObject>* object = JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->objcWrapperObjectStructure(), jsClass, 0);
+ object->setWrappedObject(wrappedObject);
+ if (JSC::JSObject* prototype = jsClass->prototype(exec))
+ object->setPrototype(exec->vm(), prototype);
+
+ return toRef(object);
+}
+
+// Make an object that is in all ways a completely vanilla JavaScript object,
+// other than that it has a native brand set that will be displayed by the default
+// Object.prototype.toString conversion.
+static JSValue *objectWithCustomBrand(JSContext *context, NSString *brand, Class cls = 0)
+{
+ JSClassDefinition definition;
+ definition = kJSClassDefinitionEmpty;
+ definition.className = [brand UTF8String];
+ JSClassRef classRef = JSClassCreate(&definition);
+ JSObjectRef result = makeWrapper([context JSGlobalContextRef], classRef, cls);
+ JSClassRelease(classRef);
+ return [JSValue valueWithJSValueRef:result inContext:context];
+}
+
+// Look for @optional properties in the prototype containing a selector to property
+// name mapping, separated by a __JS_EXPORT_AS__ delimiter.
+static NSMutableDictionary *createRenameMap(Protocol *protocol, BOOL isInstanceMethod)
+{
+ NSMutableDictionary *renameMap = [[NSMutableDictionary alloc] init];
+
+ forEachMethodInProtocol(protocol, NO, isInstanceMethod, ^(SEL sel, const char*){
+ NSString *rename = @(sel_getName(sel));
+ NSRange range = [rename rangeOfString:@"__JS_EXPORT_AS__"];
+ if (range.location == NSNotFound)
+ return;
+ NSString *selector = [rename substringToIndex:range.location];
+ NSUInteger begin = range.location + range.length;
+ NSUInteger length = [rename length] - begin - 1;
+ NSString *name = [rename substringWithRange:(NSRange){ begin, length }];
+ renameMap[selector] = name;
+ });
+
+ return renameMap;
+}
+
+inline void putNonEnumerable(JSValue *base, NSString *propertyName, JSValue *value)
+{
+ [base defineProperty:propertyName descriptor:@{
+ JSPropertyDescriptorValueKey: value,
+ JSPropertyDescriptorWritableKey: @YES,
+ JSPropertyDescriptorEnumerableKey: @NO,
+ JSPropertyDescriptorConfigurableKey: @YES
+ }];
+}
+
+// This method will iterate over the set of required methods in the protocol, and:
+// * Determine a property name (either via a renameMap or default conversion).
+// * If an accessorMap is provided, and contains this name, store the method in the map.
+// * Otherwise, if the object doesn't already contain a property with name, create it.
+static void copyMethodsToObject(JSContext *context, Class objcClass, Protocol *protocol, BOOL isInstanceMethod, JSValue *object, NSMutableDictionary *accessorMethods = nil)
+{
+ NSMutableDictionary *renameMap = createRenameMap(protocol, isInstanceMethod);
+
+ forEachMethodInProtocol(protocol, YES, isInstanceMethod, ^(SEL sel, const char* types){
+ const char* nameCStr = sel_getName(sel);
+ NSString *name = @(nameCStr);
+ if (accessorMethods && accessorMethods[name]) {
+ JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types);
+ if (!method)
+ return;
+ accessorMethods[name] = [JSValue valueWithJSValueRef:method inContext:context];
+ } else {
+ name = renameMap[name];
+ if (!name)
+ name = selectorToPropertyName(nameCStr);
+ if ([object hasProperty:name])
+ return;
+ JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types);
+ if (method)
+ putNonEnumerable(object, name, [JSValue valueWithJSValueRef:method inContext:context]);
+ }
+ });
+
+ [renameMap release];
+}
+
+static bool parsePropertyAttributes(objc_property_t property, char*& getterName, char*& setterName)
+{
+ bool readonly = false;
+ unsigned attributeCount;
+ objc_property_attribute_t* attributes = property_copyAttributeList(property, &attributeCount);
+ if (attributeCount) {
+ for (unsigned i = 0; i < attributeCount; ++i) {
+ switch (*(attributes[i].name)) {
+ case 'G':
+ getterName = strdup(attributes[i].value);
+ break;
+ case 'S':
+ setterName = strdup(attributes[i].value);
+ break;
+ case 'R':
+ readonly = true;
+ break;
+ default:
+ break;
+ }
+ }
+ free(attributes);
+ }
+ return readonly;
+}
+
+static char* makeSetterName(const char* name)
+{
+ size_t nameLength = strlen(name);
+ char* setterName = (char*)malloc(nameLength + 5); // "set" Name ":\0"
+ setterName[0] = 's';
+ setterName[1] = 'e';
+ setterName[2] = 't';
+ setterName[3] = toupper(*name);
+ memcpy(setterName + 4, name + 1, nameLength - 1);
+ setterName[nameLength + 3] = ':';
+ setterName[nameLength + 4] = '\0';
+ return setterName;
+}
+
+static void copyPrototypeProperties(JSContext *context, Class objcClass, Protocol *protocol, JSValue *prototypeValue)
+{
+ // First gather propreties into this list, then handle the methods (capturing the accessor methods).
+ struct Property {
+ const char* name;
+ char* getterName;
+ char* setterName;
+ };
+ __block Vector<Property> propertyList;
+
+ // Map recording the methods used as getters/setters.
+ NSMutableDictionary *accessorMethods = [NSMutableDictionary dictionary];
+
+ // Useful value.
+ JSValue *undefined = [JSValue valueWithUndefinedInContext:context];
+
+ forEachPropertyInProtocol(protocol, ^(objc_property_t property){
+ char* getterName = 0;
+ char* setterName = 0;
+ bool readonly = parsePropertyAttributes(property, getterName, setterName);
+ const char* name = property_getName(property);
+
+ // Add the names of the getter & setter methods to
+ if (!getterName)
+ getterName = strdup(name);
+ accessorMethods[@(getterName)] = undefined;
+ if (!readonly) {
+ if (!setterName)
+ setterName = makeSetterName(name);
+ accessorMethods[@(setterName)] = undefined;
+ }
+
+ // Add the properties to a list.
+ propertyList.append((Property){ name, getterName, setterName });
+ });
+
+ // Copy methods to the prototype, capturing accessors in the accessorMethods map.
+ copyMethodsToObject(context, objcClass, protocol, YES, prototypeValue, accessorMethods);
+
+ // Iterate the propertyList & generate accessor properties.
+ for (size_t i = 0; i < propertyList.size(); ++i) {
+ Property& property = propertyList[i];
+
+ JSValue *getter = accessorMethods[@(property.getterName)];
+ free(property.getterName);
+ ASSERT(![getter isUndefined]);
+
+ JSValue *setter = undefined;
+ if (property.setterName) {
+ setter = accessorMethods[@(property.setterName)];
+ free(property.setterName);
+ ASSERT(![setter isUndefined]);
+ }
+
+ [prototypeValue defineProperty:@(property.name) descriptor:@{
+ JSPropertyDescriptorGetKey: getter,
+ JSPropertyDescriptorSetKey: setter,
+ JSPropertyDescriptorEnumerableKey: @NO,
+ JSPropertyDescriptorConfigurableKey: @YES
+ }];
+ }
+}
+
+@interface JSObjCClassInfo : NSObject {
+ JSContext *m_context;
+ Class m_class;
+ bool m_block;
+ JSClassRef m_classRef;
+ JSC::Weak<JSC::JSObject> m_prototype;
+ JSC::Weak<JSC::JSObject> m_constructor;
+}
+
+- (id)initWithContext:(JSContext *)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo;
+- (JSValue *)wrapperForObject:(id)object;
+- (JSValue *)constructor;
+
+@end
+
+@implementation JSObjCClassInfo
+
+- (id)initWithContext:(JSContext *)context forClass:(Class)cls superClassInfo:(JSObjCClassInfo*)superClassInfo
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ const char* className = class_getName(cls);
+ m_context = context;
+ m_class = cls;
+ m_block = [cls isSubclassOfClass:getNSBlockClass()];
+ JSClassDefinition definition;
+ definition = kJSClassDefinitionEmpty;
+ definition.className = className;
+ m_classRef = JSClassCreate(&definition);
+
+ [self allocateConstructorAndPrototypeWithSuperClassInfo:superClassInfo];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ JSClassRelease(m_classRef);
+ [super dealloc];
+}
+
+- (void)allocateConstructorAndPrototypeWithSuperClassInfo:(JSObjCClassInfo*)superClassInfo
+{
+ ASSERT(!m_constructor || !m_prototype);
+ ASSERT((m_class == [NSObject class]) == !superClassInfo);
+ if (!superClassInfo) {
+ JSContextRef cContext = [m_context JSGlobalContextRef];
+ JSValue *constructor = m_context[@"Object"];
+ if (!m_constructor)
+ m_constructor = toJS(JSValueToObject(cContext, valueInternalValue(constructor), 0));
+
+ if (!m_prototype) {
+ JSValue *prototype = constructor[@"prototype"];
+ m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0));
+ }
+ } else {
+ const char* className = class_getName(m_class);
+
+ // Create or grab the prototype/constructor pair.
+ JSValue *prototype;
+ JSValue *constructor;
+ if (m_prototype)
+ prototype = [JSValue valueWithJSValueRef:toRef(m_prototype.get()) inContext:m_context];
+ else
+ prototype = objectWithCustomBrand(m_context, [NSString stringWithFormat:@"%sPrototype", className]);
+
+ if (m_constructor)
+ constructor = [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context];
+ else
+ constructor = objectWithCustomBrand(m_context, [NSString stringWithFormat:@"%sConstructor", className], m_class);
+
+ JSContextRef cContext = [m_context JSGlobalContextRef];
+ m_prototype = toJS(JSValueToObject(cContext, valueInternalValue(prototype), 0));
+ m_constructor = toJS(JSValueToObject(cContext, valueInternalValue(constructor), 0));
+
+ putNonEnumerable(prototype, @"constructor", constructor);
+ putNonEnumerable(constructor, @"prototype", prototype);
+
+ Protocol *exportProtocol = getJSExportProtocol();
+ forEachProtocolImplementingProtocol(m_class, exportProtocol, ^(Protocol *protocol){
+ copyPrototypeProperties(m_context, m_class, protocol, prototype);
+ copyMethodsToObject(m_context, m_class, protocol, NO, constructor);
+ });
+
+ // Set [Prototype].
+ JSObjectSetPrototype([m_context JSGlobalContextRef], toRef(m_prototype.get()), toRef(superClassInfo->m_prototype.get()));
+ }
+}
+
+- (void)reallocateConstructorAndOrPrototype
+{
+ [self allocateConstructorAndPrototypeWithSuperClassInfo:[m_context.wrapperMap classInfoForClass:class_getSuperclass(m_class)]];
+}
+
+- (JSValue *)wrapperForObject:(id)object
+{
+ ASSERT([object isKindOfClass:m_class]);
+ ASSERT(m_block == [object isKindOfClass:getNSBlockClass()]);
+ if (m_block) {
+ if (JSObjectRef method = objCCallbackFunctionForBlock(m_context, object))
+ return [JSValue valueWithJSValueRef:method inContext:m_context];
+ }
+
+ if (!m_prototype)
+ [self reallocateConstructorAndOrPrototype];
+ ASSERT(!!m_prototype);
+
+ JSObjectRef wrapper = makeWrapper([m_context JSGlobalContextRef], m_classRef, object);
+ JSObjectSetPrototype([m_context JSGlobalContextRef], wrapper, toRef(m_prototype.get()));
+ return [JSValue valueWithJSValueRef:wrapper inContext:m_context];
+}
+
+- (JSValue *)constructor
+{
+ if (!m_constructor)
+ [self reallocateConstructorAndOrPrototype];
+ ASSERT(!!m_constructor);
+ return [JSValue valueWithJSValueRef:toRef(m_constructor.get()) inContext:m_context];
+}
+
+@end
+
+@implementation JSWrapperMap {
+ JSContext *m_context;
+ NSMutableDictionary *m_classMap;
+ JSC::WeakGCMap<id, JSC::JSObject> m_cachedJSWrappers;
+ NSMapTable *m_cachedObjCWrappers;
+}
+
+- (id)initWithContext:(JSContext *)context
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ NSPointerFunctionsOptions keyOptions = NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality;
+ NSPointerFunctionsOptions valueOptions = NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPersonality;
+ m_cachedObjCWrappers = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:valueOptions capacity:0];
+
+ m_context = context;
+ m_classMap = [[NSMutableDictionary alloc] init];
+ return self;
+}
+
+- (void)dealloc
+{
+ [m_cachedObjCWrappers release];
+ [m_classMap release];
+ [super dealloc];
+}
+
+- (JSObjCClassInfo*)classInfoForClass:(Class)cls
+{
+ if (!cls)
+ return nil;
+
+ // Check if we've already created a JSObjCClassInfo for this Class.
+ if (JSObjCClassInfo* classInfo = (JSObjCClassInfo*)m_classMap[cls])
+ return classInfo;
+
+ // Skip internal classes beginning with '_' - just copy link to the parent class's info.
+ if ('_' == *class_getName(cls))
+ return m_classMap[cls] = [self classInfoForClass:class_getSuperclass(cls)];
+
+ return m_classMap[cls] = [[[JSObjCClassInfo alloc] initWithContext:m_context forClass:cls superClassInfo:[self classInfoForClass:class_getSuperclass(cls)]] autorelease];
+}
+
+- (JSValue *)jsWrapperForObject:(id)object
+{
+ JSC::JSObject* jsWrapper = m_cachedJSWrappers.get(object);
+ if (jsWrapper)
+ return [JSValue valueWithJSValueRef:toRef(jsWrapper) inContext:m_context];
+
+ JSValue *wrapper;
+ if (class_isMetaClass(object_getClass(object)))
+ wrapper = [[self classInfoForClass:(Class)object] constructor];
+ else {
+ JSObjCClassInfo* classInfo = [self classInfoForClass:[object class]];
+ wrapper = [classInfo wrapperForObject:object];
+ }
+
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=105891
+ // This general approach to wrapper caching is pretty effective, but there are a couple of problems:
+ // (1) For immortal objects JSValues will effectively leak and this results in error output being logged - we should avoid adding associated objects to immortal objects.
+ // (2) A long lived object may rack up many JSValues. When the contexts are released these will unprotect the associated JavaScript objects,
+ // but still, would probably nicer if we made it so that only one associated object was required, broadcasting object dealloc.
+ JSC::ExecState* exec = toJS([m_context JSGlobalContextRef]);
+ jsWrapper = toJS(exec, valueInternalValue(wrapper)).toObject(exec);
+ m_cachedJSWrappers.set(object, jsWrapper);
+ return wrapper;
+}
+
+- (JSValue *)objcWrapperForJSValueRef:(JSValueRef)value
+{
+ JSValue *wrapper = static_cast<JSValue *>(NSMapGet(m_cachedObjCWrappers, value));
+ if (!wrapper) {
+ wrapper = [[[JSValue alloc] initWithValue:value inContext:m_context] autorelease];
+ NSMapInsert(m_cachedObjCWrappers, value, wrapper);
+ }
+ return wrapper;
+}
+
+@end
+
+id tryUnwrapObjcObject(JSGlobalContextRef context, JSValueRef value)
+{
+ if (!JSValueIsObject(context, value))
+ return nil;
+ JSValueRef exception = 0;
+ JSObjectRef object = JSValueToObject(context, value, &exception);
+ ASSERT(!exception);
+ if (toJS(object)->inherits(&JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::s_info))
+ return (id)JSC::jsCast<JSC::JSAPIWrapperObject*>(toJS(object))->wrappedObject();
+ if (id target = tryUnwrapBlock(object))
+ return target;
+ return nil;
+}
+
+Protocol *getJSExportProtocol()
+{
+ static Protocol *protocol = objc_getProtocol("JSExport");
+ return protocol;
+}
+
+Class getNSBlockClass()
+{
+ static Class cls = objc_getClass("NSBlock");
+ return cls;
+}
+
+#endif
diff --git a/Source/JavaScriptCore/API/JavaScriptCore.h b/Source/JavaScriptCore/API/JavaScriptCore.h
index 87d601851..40bea9c3d 100644
--- a/Source/JavaScriptCore/API/JavaScriptCore.h
+++ b/Source/JavaScriptCore/API/JavaScriptCore.h
@@ -29,4 +29,14 @@
#include <JavaScriptCore/JavaScript.h>
#include <JavaScriptCore/JSStringRefCF.h>
+#if defined(__OBJC__) && JSC_OBJC_API_ENABLED
+
+#import "JSContext.h"
+#import "JSValue.h"
+#import "JSManagedValue.h"
+#import "JSVirtualMachine.h"
+#import "JSExport.h"
+
+#endif
+
#endif /* JavaScriptCore_h */
diff --git a/Source/JavaScriptCore/API/ObjCCallbackFunction.h b/Source/JavaScriptCore/API/ObjCCallbackFunction.h
new file mode 100644
index 000000000..0218cd8b4
--- /dev/null
+++ b/Source/JavaScriptCore/API/ObjCCallbackFunction.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef ObjCCallbackFunction_h
+#define ObjCCallbackFunction_h
+
+#include <JavaScriptCore/JSBase.h>
+
+#if JSC_OBJC_API_ENABLED
+
+#import <JavaScriptCore/JSCallbackFunction.h>
+
+#if defined(__OBJC__)
+JSObjectRef objCCallbackFunctionForMethod(JSContext *, Class, Protocol *, BOOL isInstanceMethod, SEL, const char* types);
+JSObjectRef objCCallbackFunctionForBlock(JSContext *, id);
+
+id tryUnwrapBlock(JSObjectRef);
+#endif
+
+namespace JSC {
+
+class ObjCCallbackFunctionImpl;
+
+class ObjCCallbackFunction : public JSCallbackFunction {
+public:
+ typedef JSCallbackFunction Base;
+
+ static ObjCCallbackFunction* create(ExecState*, JSGlobalObject*, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl>);
+ static void destroy(JSCell*);
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ ASSERT(globalObject);
+ return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
+ }
+
+ static JS_EXPORTDATA const ClassInfo s_info;
+
+ ObjCCallbackFunctionImpl* impl() { return m_impl.get(); }
+
+protected:
+ ObjCCallbackFunction(JSGlobalObject*, JSObjectCallAsFunctionCallback, PassOwnPtr<ObjCCallbackFunctionImpl>);
+
+private:
+ OwnPtr<ObjCCallbackFunctionImpl> m_impl;
+};
+
+} // namespace JSC
+
+#endif
+
+#endif // ObjCCallbackFunction_h
diff --git a/Source/JavaScriptCore/API/ObjCCallbackFunction.mm b/Source/JavaScriptCore/API/ObjCCallbackFunction.mm
new file mode 100644
index 000000000..cc342f59e
--- /dev/null
+++ b/Source/JavaScriptCore/API/ObjCCallbackFunction.mm
@@ -0,0 +1,615 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#import "JavaScriptCore.h"
+
+#if JSC_OBJC_API_ENABLED
+
+#import "APICast.h"
+#import "APIShims.h"
+#import "Error.h"
+#import "JSCJSValueInlines.h"
+#import "JSCell.h"
+#import "JSCellInlines.h"
+#import "JSContextInternal.h"
+#import "JSWrapperMap.h"
+#import "JSValueInternal.h"
+#import "ObjCCallbackFunction.h"
+#import "ObjcRuntimeExtras.h"
+#import <objc/runtime.h>
+#import <wtf/RetainPtr.h>
+
+class CallbackArgument {
+public:
+ virtual ~CallbackArgument();
+ virtual void set(NSInvocation *, NSInteger, JSContext *, JSValueRef, JSValueRef*) = 0;
+
+ OwnPtr<CallbackArgument> m_next;
+};
+
+CallbackArgument::~CallbackArgument()
+{
+}
+
+class CallbackArgumentBoolean : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override
+ {
+ bool value = JSValueToBoolean([context JSGlobalContextRef], argument);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+template<typename T>
+class CallbackArgumentInteger : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ T value = (T)JSC::toInt32(JSValueToNumber([context JSGlobalContextRef], argument, exception));
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+template<typename T>
+class CallbackArgumentDouble : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ T value = (T)JSValueToNumber([context JSGlobalContextRef], argument, exception);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentJSValue : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override
+ {
+ JSValue *value = [JSValue valueWithJSValueRef:argument inContext:context];
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentId : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override
+ {
+ id value = valueToObject(context, argument);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentOfClass : public CallbackArgument {
+public:
+ CallbackArgumentOfClass(Class cls)
+ : CallbackArgument()
+ , m_class(cls)
+ {
+ [m_class retain];
+ }
+
+private:
+ virtual ~CallbackArgumentOfClass()
+ {
+ [m_class release];
+ }
+
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ JSGlobalContextRef contextRef = [context JSGlobalContextRef];
+
+ id object = tryUnwrapObjcObject(contextRef, argument);
+ if (object && [object isKindOfClass:m_class]) {
+ [invocation setArgument:&object atIndex:argumentNumber];
+ return;
+ }
+
+ if (JSValueIsNull(contextRef, argument) || JSValueIsUndefined(contextRef, argument)) {
+ object = nil;
+ [invocation setArgument:&object atIndex:argumentNumber];
+ return;
+ }
+
+ *exception = toRef(JSC::createTypeError(toJS(contextRef), "Argument does not match Objective-C Class"));
+ }
+
+ Class m_class;
+};
+
+class CallbackArgumentNSNumber : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ id value = valueToNumber([context JSGlobalContextRef], argument, exception);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentNSString : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ id value = valueToString([context JSGlobalContextRef], argument, exception);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentNSDate : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ id value = valueToDate([context JSGlobalContextRef], argument, exception);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentNSArray : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ id value = valueToArray([context JSGlobalContextRef], argument, exception);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentNSDictionary : public CallbackArgument {
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef* exception) override
+ {
+ id value = valueToDictionary([context JSGlobalContextRef], argument, exception);
+ [invocation setArgument:&value atIndex:argumentNumber];
+ }
+};
+
+class CallbackArgumentStruct : public CallbackArgument {
+public:
+ CallbackArgumentStruct(NSInvocation *conversionInvocation, const char* encodedType)
+ : m_conversionInvocation(conversionInvocation)
+ , m_buffer(encodedType)
+ {
+ }
+
+private:
+ virtual void set(NSInvocation *invocation, NSInteger argumentNumber, JSContext *context, JSValueRef argument, JSValueRef*) override
+ {
+ JSValue *value = [JSValue valueWithJSValueRef:argument inContext:context];
+ [m_conversionInvocation invokeWithTarget:value];
+ [m_conversionInvocation getReturnValue:m_buffer];
+ [invocation setArgument:m_buffer atIndex:argumentNumber];
+ }
+
+ RetainPtr<NSInvocation> m_conversionInvocation;
+ StructBuffer m_buffer;
+};
+
+class ArgumentTypeDelegate {
+public:
+ typedef CallbackArgument* ResultType;
+
+ template<typename T>
+ static ResultType typeInteger()
+ {
+ return new CallbackArgumentInteger<T>;
+ }
+
+ template<typename T>
+ static ResultType typeDouble()
+ {
+ return new CallbackArgumentDouble<T>;
+ }
+
+ static ResultType typeBool()
+ {
+ return new CallbackArgumentBoolean;
+ }
+
+ static ResultType typeVoid()
+ {
+ RELEASE_ASSERT_NOT_REACHED();
+ return 0;
+ }
+
+ static ResultType typeId()
+ {
+ return new CallbackArgumentId;
+ }
+
+ static ResultType typeOfClass(const char* begin, const char* end)
+ {
+ StringRange copy(begin, end);
+ Class cls = objc_getClass(copy);
+ if (!cls)
+ return 0;
+
+ if (cls == [JSValue class])
+ return new CallbackArgumentJSValue;
+ if (cls == [NSString class])
+ return new CallbackArgumentNSString;
+ if (cls == [NSNumber class])
+ return new CallbackArgumentNSNumber;
+ if (cls == [NSDate class])
+ return new CallbackArgumentNSDate;
+ if (cls == [NSArray class])
+ return new CallbackArgumentNSArray;
+ if (cls == [NSDictionary class])
+ return new CallbackArgumentNSDictionary;
+
+ return new CallbackArgumentOfClass(cls);
+ }
+
+ static ResultType typeBlock(const char*, const char*)
+ {
+ return nil;
+ }
+
+ static ResultType typeStruct(const char* begin, const char* end)
+ {
+ StringRange copy(begin, end);
+ if (NSInvocation *invocation = valueToTypeInvocationFor(copy))
+ return new CallbackArgumentStruct(invocation, copy);
+ return 0;
+ }
+};
+
+class CallbackResult {
+public:
+ virtual ~CallbackResult()
+ {
+ }
+
+ virtual JSValueRef get(NSInvocation *, JSContext *, JSValueRef*) = 0;
+};
+
+class CallbackResultVoid : public CallbackResult {
+ virtual JSValueRef get(NSInvocation *, JSContext *context, JSValueRef*) override
+ {
+ return JSValueMakeUndefined([context JSGlobalContextRef]);
+ }
+};
+
+class CallbackResultId : public CallbackResult {
+ virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override
+ {
+ id value;
+ [invocation getReturnValue:&value];
+ return objectToValue(context, value);
+ }
+};
+
+template<typename T>
+class CallbackResultNumeric : public CallbackResult {
+ virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override
+ {
+ T value;
+ [invocation getReturnValue:&value];
+ return JSValueMakeNumber([context JSGlobalContextRef], value);
+ }
+};
+
+class CallbackResultBoolean : public CallbackResult {
+ virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override
+ {
+ bool value;
+ [invocation getReturnValue:&value];
+ return JSValueMakeBoolean([context JSGlobalContextRef], value);
+ }
+};
+
+class CallbackResultStruct : public CallbackResult {
+public:
+ CallbackResultStruct(NSInvocation *conversionInvocation, const char* encodedType)
+ : m_conversionInvocation(conversionInvocation)
+ , m_buffer(encodedType)
+ {
+ }
+
+private:
+ virtual JSValueRef get(NSInvocation *invocation, JSContext *context, JSValueRef*) override
+ {
+ [invocation getReturnValue:m_buffer];
+
+ [m_conversionInvocation setArgument:m_buffer atIndex:2];
+ [m_conversionInvocation setArgument:&context atIndex:3];
+ [m_conversionInvocation invokeWithTarget:[JSValue class]];
+
+ JSValue *value;
+ [m_conversionInvocation getReturnValue:&value];
+ return valueInternalValue(value);
+ }
+
+ RetainPtr<NSInvocation> m_conversionInvocation;
+ StructBuffer m_buffer;
+};
+
+class ResultTypeDelegate {
+public:
+ typedef CallbackResult* ResultType;
+
+ template<typename T>
+ static ResultType typeInteger()
+ {
+ return new CallbackResultNumeric<T>;
+ }
+
+ template<typename T>
+ static ResultType typeDouble()
+ {
+ return new CallbackResultNumeric<T>;
+ }
+
+ static ResultType typeBool()
+ {
+ return new CallbackResultBoolean;
+ }
+
+ static ResultType typeVoid()
+ {
+ return new CallbackResultVoid;
+ }
+
+ static ResultType typeId()
+ {
+ return new CallbackResultId();
+ }
+
+ static ResultType typeOfClass(const char*, const char*)
+ {
+ return new CallbackResultId();
+ }
+
+ static ResultType typeBlock(const char*, const char*)
+ {
+ return new CallbackResultId();
+ }
+
+ static ResultType typeStruct(const char* begin, const char* end)
+ {
+ StringRange copy(begin, end);
+ if (NSInvocation *invocation = typeToValueInvocationFor(copy))
+ return new CallbackResultStruct(invocation, copy);
+ return 0;
+ }
+};
+
+enum CallbackType {
+ CallbackInstanceMethod,
+ CallbackClassMethod,
+ CallbackBlock
+};
+
+namespace JSC {
+
+class ObjCCallbackFunctionImpl {
+public:
+ ObjCCallbackFunctionImpl(JSContext *context, NSInvocation *invocation, CallbackType type, Class instanceClass, PassOwnPtr<CallbackArgument> arguments, PassOwnPtr<CallbackResult> result)
+ : m_context(context)
+ , m_type(type)
+ , m_instanceClass([instanceClass retain])
+ , m_invocation(invocation)
+ , m_arguments(arguments)
+ , m_result(result)
+ {
+ ASSERT(type != CallbackInstanceMethod || instanceClass);
+ }
+
+ ~ObjCCallbackFunctionImpl()
+ {
+ if (m_type != CallbackInstanceMethod)
+ [[m_invocation.get() target] release];
+ [m_instanceClass release];
+ }
+
+ JSValueRef call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
+
+ JSContext *context()
+ {
+ return m_context.get();
+ }
+
+ void setContext(JSContext *context)
+ {
+ ASSERT(!m_context.get());
+ m_context.set(context);
+ }
+
+ id wrappedBlock()
+ {
+ return m_type == CallbackBlock ? [m_invocation target] : nil;
+ }
+
+private:
+ WeakContextRef m_context;
+ CallbackType m_type;
+ Class m_instanceClass;
+ RetainPtr<NSInvocation> m_invocation;
+ OwnPtr<CallbackArgument> m_arguments;
+ OwnPtr<CallbackResult> m_result;
+};
+
+static JSValueRef objCCallbackFunctionCallAsFunction(JSContextRef callerContext, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ // Retake the API lock - we need this for a few reasons:
+ // (1) We don't want to support the C-API's confusing drops-locks-once policy - should only drop locks if we can do so recursively.
+ // (2) We're calling some JSC internals that require us to be on the 'inside' - e.g. createTypeError.
+ // (3) We need to be locked (per context would be fine) against conflicting usage of the ObjCCallbackFunction's NSInvocation.
+ JSC::APIEntryShim entryShim(toJS(callerContext));
+
+ ObjCCallbackFunction* callback = static_cast<ObjCCallbackFunction*>(toJS(function));
+ ObjCCallbackFunctionImpl* impl = callback->impl();
+ JSContext *context = impl->context();
+ if (!context) {
+ context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(toJS(callerContext)->lexicalGlobalObject()->globalExec())];
+ impl->setContext(context);
+ }
+
+ CallbackData callbackData;
+ JSValueRef result;
+ @autoreleasepool {
+ [context beginCallbackWithData:&callbackData thisValue:thisObject argumentCount:argumentCount arguments:arguments];
+ result = impl->call(context, thisObject, argumentCount, arguments, exception);
+ if (context.exception)
+ *exception = valueInternalValue(context.exception);
+ [context endCallbackWithData:&callbackData];
+ }
+ return result;
+}
+
+const JSC::ClassInfo ObjCCallbackFunction::s_info = { "CallbackFunction", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(ObjCCallbackFunction) };
+
+ObjCCallbackFunction::ObjCCallbackFunction(JSC::JSGlobalObject* globalObject, JSObjectCallAsFunctionCallback callback, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
+ : Base(globalObject, globalObject->objcCallbackFunctionStructure(), callback)
+ , m_impl(impl)
+{
+}
+
+ObjCCallbackFunction* ObjCCallbackFunction::create(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, const String& name, PassOwnPtr<ObjCCallbackFunctionImpl> impl)
+{
+ ObjCCallbackFunction* function = new (NotNull, allocateCell<ObjCCallbackFunction>(*exec->heap())) ObjCCallbackFunction(globalObject, objCCallbackFunctionCallAsFunction, impl);
+ function->finishCreation(exec->vm(), name);
+ return function;
+}
+
+void ObjCCallbackFunction::destroy(JSCell* cell)
+{
+ static_cast<ObjCCallbackFunction*>(cell)->ObjCCallbackFunction::~ObjCCallbackFunction();
+}
+
+JSValueRef ObjCCallbackFunctionImpl::call(JSContext *context, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ JSGlobalContextRef contextRef = [context JSGlobalContextRef];
+
+ size_t firstArgument;
+ switch (m_type) {
+ case CallbackInstanceMethod: {
+ id target = tryUnwrapObjcObject(contextRef, thisObject);
+ if (!target || ![target isKindOfClass:m_instanceClass]) {
+ *exception = toRef(JSC::createTypeError(toJS(contextRef), "self type check failed for Objective-C instance method"));
+ return JSValueMakeUndefined(contextRef);
+ }
+ [m_invocation setTarget:target];
+ }
+ // fallthrough - firstArgument for CallbackInstanceMethod is also 2!
+ case CallbackClassMethod:
+ firstArgument = 2;
+ break;
+ case CallbackBlock:
+ firstArgument = 1;
+ }
+
+ size_t argumentNumber = 0;
+ for (CallbackArgument* argument = m_arguments.get(); argument; argument = argument->m_next.get()) {
+ JSValueRef value = argumentNumber < argumentCount ? arguments[argumentNumber] : JSValueMakeUndefined(contextRef);
+ argument->set(m_invocation.get(), argumentNumber + firstArgument, context, value, exception);
+ if (*exception)
+ return JSValueMakeUndefined(contextRef);
+ ++argumentNumber;
+ }
+
+ [m_invocation invoke];
+
+ return m_result->get(m_invocation.get(), context, exception);
+}
+
+} // namespace JSC
+
+static bool blockSignatureContainsClass()
+{
+ static bool containsClass = ^{
+ id block = ^(NSString *string){ return string; };
+ return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
+ }();
+ return containsClass;
+}
+
+inline bool skipNumber(const char*& position)
+{
+ if (!isASCIIDigit(*position))
+ return false;
+ while (isASCIIDigit(*++position)) { }
+ return true;
+}
+
+static JSObjectRef objCCallbackFunctionForInvocation(JSContext *context, NSInvocation *invocation, CallbackType type, Class instanceClass, const char* signatureWithObjcClasses)
+{
+ const char* position = signatureWithObjcClasses;
+
+ OwnPtr<CallbackResult> result = adoptPtr(parseObjCType<ResultTypeDelegate>(position));
+ if (!result || !skipNumber(position))
+ return nil;
+
+ switch (type) {
+ case CallbackInstanceMethod:
+ case CallbackClassMethod:
+ // Methods are passed two implicit arguments - (id)self, and the selector.
+ if ('@' != *position++ || !skipNumber(position) || ':' != *position++ || !skipNumber(position))
+ return nil;
+ break;
+ case CallbackBlock:
+ // Blocks are passed one implicit argument - the block, of type "@?".
+ if (('@' != *position++) || ('?' != *position++) || !skipNumber(position))
+ return nil;
+ // Only allow arguments of type 'id' if the block signature contains the NS type information.
+ if ((!blockSignatureContainsClass() && strchr(position, '@')))
+ return nil;
+ break;
+ }
+
+ OwnPtr<CallbackArgument> arguments = 0;
+ OwnPtr<CallbackArgument>* nextArgument = &arguments;
+ unsigned argumentCount = 0;
+ while (*position) {
+ OwnPtr<CallbackArgument> argument = adoptPtr(parseObjCType<ArgumentTypeDelegate>(position));
+ if (!argument || !skipNumber(position))
+ return nil;
+
+ *nextArgument = argument.release();
+ nextArgument = &(*nextArgument)->m_next;
+ ++argumentCount;
+ }
+
+ JSC::ExecState* exec = toJS([context JSGlobalContextRef]);
+ JSC::APIEntryShim shim(exec);
+ OwnPtr<JSC::ObjCCallbackFunctionImpl> impl = adoptPtr(new JSC::ObjCCallbackFunctionImpl(context, invocation, type, instanceClass, arguments.release(), result.release()));
+ // FIXME: Maybe we could support having the selector as the name of the function to make it a bit more user-friendly from the JS side?
+ return toRef(JSC::ObjCCallbackFunction::create(exec, exec->lexicalGlobalObject(), "", impl.release()));
+}
+
+JSObjectRef objCCallbackFunctionForMethod(JSContext *context, Class cls, Protocol *protocol, BOOL isInstanceMethod, SEL sel, const char* types)
+{
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:types]];
+ [invocation setSelector:sel];
+ if (!isInstanceMethod)
+ [invocation setTarget:cls];
+ return objCCallbackFunctionForInvocation(context, invocation, isInstanceMethod ? CallbackInstanceMethod : CallbackClassMethod, isInstanceMethod ? cls : nil, _protocol_getMethodTypeEncoding(protocol, sel, YES, isInstanceMethod));
+}
+
+JSObjectRef objCCallbackFunctionForBlock(JSContext *context, id target)
+{
+ if (!_Block_has_signature(target))
+ return 0;
+ const char* signature = _Block_signature(target);
+ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:signature]];
+ [invocation setTarget:[target copy]];
+ return objCCallbackFunctionForInvocation(context, invocation, CallbackBlock, nil, signature);
+}
+
+id tryUnwrapBlock(JSObjectRef object)
+{
+ if (!toJS(object)->inherits(&JSC::ObjCCallbackFunction::s_info))
+ return nil;
+ return static_cast<JSC::ObjCCallbackFunction*>(toJS(object))->impl()->wrappedBlock();
+}
+
+#endif
diff --git a/Source/JavaScriptCore/API/ObjcRuntimeExtras.h b/Source/JavaScriptCore/API/ObjcRuntimeExtras.h
new file mode 100644
index 000000000..48c112093
--- /dev/null
+++ b/Source/JavaScriptCore/API/ObjcRuntimeExtras.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <objc/runtime.h>
+#import <wtf/HashSet.h>
+#import <wtf/Vector.h>
+
+inline bool protocolImplementsProtocol(Protocol *candidate, Protocol *target)
+{
+ unsigned protocolProtocolsCount;
+ Protocol ** protocolProtocols = protocol_copyProtocolList(candidate, &protocolProtocolsCount);
+ for (unsigned i = 0; i < protocolProtocolsCount; ++i) {
+ if (protocol_isEqual(protocolProtocols[i], target)) {
+ free(protocolProtocols);
+ return true;
+ }
+ }
+ free(protocolProtocols);
+ return false;
+}
+
+inline void forEachProtocolImplementingProtocol(Class cls, Protocol *target, void (^callback)(Protocol *))
+{
+ ASSERT(cls);
+ ASSERT(target);
+
+ Vector<Protocol *> worklist;
+ HashSet<void*> visited;
+
+ // Initially fill the worklist with the Class's protocols.
+ unsigned protocolsCount;
+ Protocol ** protocols = class_copyProtocolList(cls, &protocolsCount);
+ worklist.append(protocols, protocolsCount);
+ free(protocols);
+
+ while (!worklist.isEmpty()) {
+ Protocol *protocol = worklist.last();
+ worklist.removeLast();
+
+ // Are we encountering this Protocol for the first time?
+ if (!visited.add(protocol).isNewEntry)
+ continue;
+
+ // If it implements the protocol, make the callback.
+ if (protocolImplementsProtocol(protocol, target))
+ callback(protocol);
+
+ // Add incorporated protocols to the worklist.
+ protocols = protocol_copyProtocolList(protocol, &protocolsCount);
+ worklist.append(protocols, protocolsCount);
+ free(protocols);
+ }
+}
+
+inline void forEachMethodInClass(Class cls, void (^callback)(Method))
+{
+ unsigned count;
+ Method* methods = class_copyMethodList(cls, &count);
+ for (unsigned i = 0; i < count; ++i)
+ callback(methods[i]);
+ free(methods);
+}
+
+inline void forEachMethodInProtocol(Protocol *protocol, BOOL isRequiredMethod, BOOL isInstanceMethod, void (^callback)(SEL, const char*))
+{
+ unsigned count;
+ struct objc_method_description* methods = protocol_copyMethodDescriptionList(protocol, isRequiredMethod, isInstanceMethod, &count);
+ for (unsigned i = 0; i < count; ++i)
+ callback(methods[i].name, methods[i].types);
+ free(methods);
+}
+
+inline void forEachPropertyInProtocol(Protocol *protocol, void (^callback)(objc_property_t))
+{
+ unsigned count;
+ objc_property_t* properties = protocol_copyPropertyList(protocol, &count);
+ for (unsigned i = 0; i < count; ++i)
+ callback(properties[i]);
+ free(properties);
+}
+
+template<char open, char close>
+void skipPair(const char*& position)
+{
+ size_t count = 1;
+ do {
+ char c = *position++;
+ if (!c)
+ @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Malformed type encoding" userInfo:nil];
+ if (c == open)
+ ++count;
+ else if (c == close)
+ --count;
+ } while (count);
+}
+
+class StringRange {
+ WTF_MAKE_NONCOPYABLE(StringRange);
+public:
+ StringRange(const char* begin, const char* end) : m_ptr(strndup(begin, end - begin)) { }
+ ~StringRange() { free(m_ptr); }
+ operator const char*() const { return m_ptr; }
+ const char* get() const { return m_ptr; }
+
+private:
+ char* m_ptr;
+};
+
+class StructBuffer {
+ WTF_MAKE_NONCOPYABLE(StructBuffer);
+public:
+ StructBuffer(const char* encodedType)
+ {
+ NSUInteger size, alignment;
+ NSGetSizeAndAlignment(encodedType, &size, &alignment);
+ --alignment;
+ m_allocation = static_cast<char*>(malloc(size + alignment));
+ m_buffer = reinterpret_cast<char*>((reinterpret_cast<intptr_t>(m_allocation) + alignment) & ~alignment);
+ }
+
+ ~StructBuffer() { free(m_allocation); }
+ operator void*() const { return m_buffer; }
+
+private:
+ void* m_allocation;
+ void* m_buffer;
+};
+
+template<typename DelegateType>
+typename DelegateType::ResultType parseObjCType(const char*& position)
+{
+ ASSERT(*position);
+
+ switch (*position++) {
+ case 'c':
+ return DelegateType::template typeInteger<char>();
+ case 'i':
+ return DelegateType::template typeInteger<int>();
+ case 's':
+ return DelegateType::template typeInteger<short>();
+ case 'l':
+ return DelegateType::template typeInteger<long>();
+ case 'q':
+ return DelegateType::template typeDouble<unsigned long long>();
+ case 'C':
+ return DelegateType::template typeInteger<unsigned char>();
+ case 'I':
+ return DelegateType::template typeInteger<unsigned>();
+ case 'S':
+ return DelegateType::template typeInteger<unsigned short>();
+ case 'L':
+ return DelegateType::template typeInteger<unsigned long>();
+ case 'Q':
+ return DelegateType::template typeDouble<unsigned long long>();
+ case 'f':
+ return DelegateType::template typeDouble<float>();
+ case 'd':
+ return DelegateType::template typeDouble<double>();
+ case 'B':
+ return DelegateType::typeBool();
+ case 'v':
+ return DelegateType::typeVoid();
+
+ case '@': { // An object (whether statically typed or typed id)
+ if (position[0] == '?' && position[1] == '<') {
+ position += 2;
+ const char* begin = position;
+ skipPair<'<','>'>(position);
+ return DelegateType::typeBlock(begin, position - 1);
+ }
+
+ if (*position == '"') {
+ const char* begin = ++position;
+ position = index(position, '"');
+ return DelegateType::typeOfClass(begin, position++);
+ }
+
+ return DelegateType::typeId();
+ }
+
+ case '{': { // {name=type...} A structure
+ const char* begin = position - 1;
+ skipPair<'{','}'>(position);
+ return DelegateType::typeStruct(begin, position);
+ }
+
+ // NOT supporting C strings, arrays, pointers, unions, bitfields, function pointers.
+ case '*': // A character string (char *)
+ case '[': // [array type] An array
+ case '(': // (name=type...) A union
+ case 'b': // bnum A bit field of num bits
+ case '^': // ^type A pointer to type
+ case '?': // An unknown type (among other things, this code is used for function pointers)
+ // NOT supporting Objective-C Class, SEL
+ case '#': // A class object (Class)
+ case ':': // A method selector (SEL)
+ default:
+ return nil;
+ }
+}
+
+extern "C" {
+ // Forward declare some Objective-C runtime internal methods that are not API.
+ const char *_protocol_getMethodTypeEncoding(Protocol *, SEL, BOOL isRequiredMethod, BOOL isInstanceMethod);
+ id objc_initWeak(id *, id);
+ void objc_destroyWeak(id *);
+ bool _Block_has_signature(void *);
+ const char * _Block_signature(void *);
+}
diff --git a/Source/JavaScriptCore/API/OpaqueJSString.cpp b/Source/JavaScriptCore/API/OpaqueJSString.cpp
index 90bc1c095..a7cef8d9a 100644
--- a/Source/JavaScriptCore/API/OpaqueJSString.cpp
+++ b/Source/JavaScriptCore/API/OpaqueJSString.cpp
@@ -48,7 +48,7 @@ String OpaqueJSString::string() const
return m_string.isolatedCopy();
}
-Identifier OpaqueJSString::identifier(JSGlobalData* globalData) const
+Identifier OpaqueJSString::identifier(VM* vm) const
{
if (!this || m_string.isNull())
return Identifier();
@@ -57,7 +57,7 @@ Identifier OpaqueJSString::identifier(JSGlobalData* globalData) const
return Identifier(Identifier::EmptyIdentifier);
if (m_string.is8Bit())
- return Identifier(globalData, m_string.characters8(), m_string.length());
+ return Identifier(vm, m_string.characters8(), m_string.length());
- return Identifier(globalData, m_string.characters16(), m_string.length());
+ return Identifier(vm, m_string.characters16(), m_string.length());
}
diff --git a/Source/JavaScriptCore/API/OpaqueJSString.h b/Source/JavaScriptCore/API/OpaqueJSString.h
index 0464e8813..c374b567a 100644
--- a/Source/JavaScriptCore/API/OpaqueJSString.h
+++ b/Source/JavaScriptCore/API/OpaqueJSString.h
@@ -31,7 +31,7 @@
namespace JSC {
class Identifier;
- class JSGlobalData;
+ class VM;
}
struct OpaqueJSString : public ThreadSafeRefCounted<OpaqueJSString> {
@@ -57,7 +57,10 @@ struct OpaqueJSString : public ThreadSafeRefCounted<OpaqueJSString> {
unsigned length() { return !!this ? m_string.length() : 0; }
JS_EXPORT_PRIVATE String string() const;
- JSC::Identifier identifier(JSC::JSGlobalData*) const;
+ JSC::Identifier identifier(JSC::VM*) const;
+#if PLATFORM(QT)
+ QString qString() const { return m_string; }
+#endif
private:
friend class WTF::ThreadSafeRefCounted<OpaqueJSString>;
@@ -67,12 +70,8 @@ private:
}
OpaqueJSString(const String& string)
+ : m_string(string.isolatedCopy())
{
- // Make a copy of the source string.
- if (string.is8Bit())
- m_string = String(string.characters8(), string.length());
- else
- m_string = String(string.characters16(), string.length());
}
OpaqueJSString(const LChar* characters, unsigned length)
diff --git a/Source/JavaScriptCore/API/tests/JSNode.c b/Source/JavaScriptCore/API/tests/JSNode.c
index 052c88a02..d9a40bea6 100644
--- a/Source/JavaScriptCore/API/tests/JSNode.c
+++ b/Source/JavaScriptCore/API/tests/JSNode.c
@@ -30,7 +30,6 @@
#include "JSValueRef.h"
#include "Node.h"
#include "NodeList.h"
-#include <wtf/UnusedParam.h>
#include <wtf/Assertions.h>
static JSValueRef JSNode_appendChild(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
diff --git a/Source/JavaScriptCore/API/tests/JSNodeList.c b/Source/JavaScriptCore/API/tests/JSNodeList.c
index 0d194845e..61d7041a4 100644
--- a/Source/JavaScriptCore/API/tests/JSNodeList.c
+++ b/Source/JavaScriptCore/API/tests/JSNodeList.c
@@ -27,7 +27,6 @@
#include "JSNodeList.h"
#include "JSObjectRef.h"
#include "JSValueRef.h"
-#include <wtf/UnusedParam.h>
#include <wtf/Assertions.h>
static JSValueRef JSNodeList_item(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
diff --git a/Source/JavaScriptCore/API/tests/minidom.c b/Source/JavaScriptCore/API/tests/minidom.c
index 43ae2c1a8..8614e51e9 100644
--- a/Source/JavaScriptCore/API/tests/minidom.c
+++ b/Source/JavaScriptCore/API/tests/minidom.c
@@ -31,7 +31,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <wtf/Assertions.h>
-#include <wtf/UnusedParam.h>
static char* createStringWithContentsOfFile(const char* fileName);
static JSValueRef print(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);
diff --git a/Source/JavaScriptCore/API/tests/testapi.c b/Source/JavaScriptCore/API/tests/testapi.c
index c2400f7ec..be18b2bf7 100644
--- a/Source/JavaScriptCore/API/tests/testapi.c
+++ b/Source/JavaScriptCore/API/tests/testapi.c
@@ -27,10 +27,17 @@
#include "JSBasePrivate.h"
#include "JSContextRefPrivate.h"
#include "JSObjectRefPrivate.h"
+#include "JSScriptRefPrivate.h"
+#include "JSStringRefPrivate.h"
#include <math.h>
#define ASSERT_DISABLED 0
#include <wtf/Assertions.h>
-#include <wtf/UnusedParam.h>
+
+#if PLATFORM(MAC) || PLATFORM(IOS)
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#endif
#if OS(WINDOWS)
#include <windows.h>
@@ -45,10 +52,19 @@ static double nan(const char*)
return std::numeric_limits<double>::quiet_NaN();
}
+using std::isinf;
+using std::isnan;
+
#endif
+#if JSC_OBJC_API_ENABLED
+void testObjectiveCAPI(void);
+#endif
+
+extern void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+
static JSGlobalContextRef context;
-static int failed;
+int failed;
static void assertEqualsAsBoolean(JSValueRef value, bool expectedValue)
{
if (JSValueToBoolean(context, value) != expectedValue) {
@@ -481,6 +497,11 @@ static bool PropertyCatchalls_setProperty(JSContextRef context, JSObjectRef obje
return true;
}
+ if (JSStringIsEqualToUTF8CString(propertyName, "make_throw") || JSStringIsEqualToUTF8CString(propertyName, "0")) {
+ *exception = JSValueMakeNumber(context, 5);
+ return true;
+ }
+
return false;
}
@@ -1030,6 +1051,68 @@ static void checkConstnessInJSObjectNames()
val.name = "something";
}
+#if PLATFORM(MAC) || PLATFORM(IOS)
+static double currentCPUTime()
+{
+ mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
+ thread_basic_info_data_t info;
+
+ /* Get thread information */
+ mach_port_t threadPort = mach_thread_self();
+ thread_info(threadPort, THREAD_BASIC_INFO, (thread_info_t)(&info), &infoCount);
+ mach_port_deallocate(mach_task_self(), threadPort);
+
+ double time = info.user_time.seconds + info.user_time.microseconds / 1000000.;
+ time += info.system_time.seconds + info.system_time.microseconds / 1000000.;
+
+ return time;
+}
+
+static JSValueRef currentCPUTime_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+ UNUSED_PARAM(functionObject);
+ UNUSED_PARAM(thisObject);
+ UNUSED_PARAM(argumentCount);
+ UNUSED_PARAM(arguments);
+ UNUSED_PARAM(exception);
+
+ ASSERT(JSContextGetGlobalContext(ctx) == context);
+ return JSValueMakeNumber(ctx, currentCPUTime());
+}
+
+bool shouldTerminateCallbackWasCalled = false;
+static bool shouldTerminateCallback(JSContextRef ctx, void* context)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(context);
+ shouldTerminateCallbackWasCalled = true;
+ return true;
+}
+
+bool cancelTerminateCallbackWasCalled = false;
+static bool cancelTerminateCallback(JSContextRef ctx, void* context)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(context);
+ cancelTerminateCallbackWasCalled = true;
+ return false;
+}
+
+int extendTerminateCallbackCalled = 0;
+static bool extendTerminateCallback(JSContextRef ctx, void* context)
+{
+ UNUSED_PARAM(context);
+ extendTerminateCallbackCalled++;
+ if (extendTerminateCallbackCalled == 1) {
+ JSContextGroupRef contextGroup = JSContextGetGroup(ctx);
+ JSContextGroupSetExecutionTimeLimit(contextGroup, .200f, extendTerminateCallback, 0);
+ return false;
+ }
+ return true;
+}
+#endif /* PLATFORM(MAC) || PLATFORM(IOS) */
+
+
int main(int argc, char* argv[])
{
#if OS(WINDOWS)
@@ -1039,6 +1122,10 @@ int main(int argc, char* argv[])
::SetErrorMode(0);
#endif
+#if JSC_OBJC_API_ENABLED
+ testObjectiveCAPI();
+#endif
+
const char *scriptPath = "testapi.js";
if (argc > 1) {
scriptPath = argv[1];
@@ -1061,6 +1148,8 @@ int main(int argc, char* argv[])
JSClassRef globalObjectClass = JSClassCreate(&globalObjectClassDefinition);
context = JSGlobalContextCreateInGroup(NULL, globalObjectClass);
+ JSContextGroupRef contextGroup = JSContextGetGroup(context);
+
JSGlobalContextRetain(context);
JSGlobalContextRelease(context);
ASSERT(JSContextGetGlobalContext(context) == context);
@@ -1117,6 +1206,12 @@ int main(int argc, char* argv[])
free(buffer);
JSValueRef jsCFEmptyStringWithCharacters = JSValueMakeString(context, jsCFEmptyIStringWithCharacters);
+ JSChar constantString[] = { 'H', 'e', 'l', 'l', 'o', };
+ JSStringRef constantStringRef = JSStringCreateWithCharactersNoCopy(constantString, sizeof(constantString) / sizeof(constantString[0]));
+ ASSERT(JSStringGetCharactersPtr(constantStringRef) == constantString);
+ JSStringRelease(constantStringRef);
+
+ ASSERT(JSValueGetType(context, NULL) == kJSTypeNull);
ASSERT(JSValueGetType(context, jsUndefined) == kJSTypeUndefined);
ASSERT(JSValueGetType(context, jsNull) == kJSTypeNull);
ASSERT(JSValueGetType(context, jsTrue) == kJSTypeBoolean);
@@ -1131,6 +1226,17 @@ int main(int argc, char* argv[])
ASSERT(JSValueGetType(context, jsCFEmptyString) == kJSTypeString);
ASSERT(JSValueGetType(context, jsCFEmptyStringWithCharacters) == kJSTypeString);
+ ASSERT(!JSValueIsBoolean(context, NULL));
+ ASSERT(!JSValueIsObject(context, NULL));
+ ASSERT(!JSValueIsString(context, NULL));
+ ASSERT(!JSValueIsNumber(context, NULL));
+ ASSERT(!JSValueIsUndefined(context, NULL));
+ ASSERT(JSValueIsNull(context, NULL));
+ ASSERT(!JSObjectCallAsFunction(context, NULL, NULL, 0, NULL, NULL));
+ ASSERT(!JSObjectCallAsConstructor(context, NULL, 0, NULL, NULL));
+ ASSERT(!JSObjectIsConstructor(context, NULL));
+ ASSERT(!JSObjectIsFunction(context, NULL));
+
JSStringRef nullString = JSStringCreateWithUTF8CString(0);
const JSChar* characters = JSStringGetCharactersPtr(nullString);
if (characters) {
@@ -1139,6 +1245,14 @@ int main(int argc, char* argv[])
} else
printf("PASS: returned null when accessing character pointer of a null String.\n");
+ JSStringRef emptyString = JSStringCreateWithCFString(CFSTR(""));
+ characters = JSStringGetCharactersPtr(emptyString);
+ if (!characters) {
+ printf("FAIL: Returned null when accessing character pointer of an empty String.\n");
+ failed = 1;
+ } else
+ printf("PASS: returned empty when accessing character pointer of an empty String.\n");
+
size_t length = JSStringGetLength(nullString);
if (length) {
printf("FAIL: Didn't return 0 length for null String.\n");
@@ -1147,6 +1261,14 @@ int main(int argc, char* argv[])
printf("PASS: returned 0 length for null String.\n");
JSStringRelease(nullString);
+ length = JSStringGetLength(emptyString);
+ if (length) {
+ printf("FAIL: Didn't return 0 length for empty String.\n");
+ failed = 1;
+ } else
+ printf("PASS: returned 0 length for empty String.\n");
+ JSStringRelease(emptyString);
+
JSObjectRef propertyCatchalls = JSObjectMake(context, PropertyCatchalls_class(context), NULL);
JSStringRef propertyCatchallsString = JSStringCreateWithUTF8CString("PropertyCatchalls");
JSObjectSetProperty(context, globalObject, propertyCatchallsString, propertyCatchalls, kJSPropertyAttributeNone, NULL);
@@ -1399,9 +1521,12 @@ int main(int argc, char* argv[])
JSValueUnprotect(context, jsNumberValue);
JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;");
- JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;");
+ const char* badSyntaxConstant = "x := 1;";
+ JSStringRef badSyntax = JSStringCreateWithUTF8CString(badSyntaxConstant);
ASSERT(JSCheckScriptSyntax(context, goodSyntax, NULL, 0, NULL));
ASSERT(!JSCheckScriptSyntax(context, badSyntax, NULL, 0, NULL));
+ ASSERT(!JSScriptCreateFromString(contextGroup, 0, 0, badSyntax, 0, 0));
+ ASSERT(!JSScriptCreateReferencingImmortalASCIIText(contextGroup, 0, 0, badSyntaxConstant, strlen(badSyntaxConstant), 0, 0));
JSValueRef result;
JSValueRef v;
@@ -1590,13 +1715,21 @@ int main(int argc, char* argv[])
v = JSObjectCallAsFunction(context, function, o, 0, NULL, NULL);
ASSERT(JSValueIsEqual(context, v, o, NULL));
- JSStringRef script = JSStringCreateWithUTF8CString("this;");
+ const char* thisScript = "this;";
+ JSStringRef script = JSStringCreateWithUTF8CString(thisScript);
v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL);
ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
v = JSEvaluateScript(context, script, o, NULL, 1, NULL);
ASSERT(JSValueIsEqual(context, v, o, NULL));
JSStringRelease(script);
+ JSScriptRef scriptObject = JSScriptCreateReferencingImmortalASCIIText(contextGroup, 0, 0, thisScript, strlen(thisScript), 0, 0);
+ v = JSScriptEvaluate(context, scriptObject, NULL, NULL);
+ ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
+ v = JSScriptEvaluate(context, scriptObject, o, NULL);
+ ASSERT(JSValueIsEqual(context, v, o, NULL));
+ JSScriptRelease(scriptObject);
+
script = JSStringCreateWithUTF8CString("eval(this);");
v = JSEvaluateScript(context, script, NULL, NULL, 1, NULL);
ASSERT(JSValueIsEqual(context, v, globalObject, NULL));
@@ -1616,8 +1749,23 @@ int main(int argc, char* argv[])
printf("FAIL: Test script could not be loaded.\n");
failed = 1;
} else {
- script = JSStringCreateWithUTF8CString(scriptUTF8);
- result = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ JSStringRef url = JSStringCreateWithUTF8CString(scriptPath);
+ JSStringRef script = JSStringCreateWithUTF8CString(scriptUTF8);
+ JSStringRef errorMessage = 0;
+ int errorLine = 0;
+ JSScriptRef scriptObject = JSScriptCreateFromString(contextGroup, url, 1, script, &errorMessage, &errorLine);
+ ASSERT((!scriptObject) != (!errorMessage));
+ if (!scriptObject) {
+ printf("FAIL: Test script did not parse\n\t%s:%d\n\t", scriptPath, errorLine);
+ CFStringRef errorCF = JSStringCopyCFString(kCFAllocatorDefault, errorMessage);
+ CFShow(errorCF);
+ CFRelease(errorCF);
+ JSStringRelease(errorMessage);
+ failed = 1;
+ }
+
+ JSStringRelease(script);
+ result = scriptObject ? JSScriptEvaluate(context, scriptObject, 0, &exception) : 0;
if (result && JSValueIsUndefined(context, result))
printf("PASS: Test script executed successfully.\n");
else {
@@ -1629,10 +1777,163 @@ int main(int argc, char* argv[])
JSStringRelease(exceptionIString);
failed = 1;
}
- JSStringRelease(script);
+ JSScriptRelease(scriptObject);
free(scriptUTF8);
}
+#if PLATFORM(MAC) || PLATFORM(IOS)
+ JSStringRef currentCPUTimeStr = JSStringCreateWithUTF8CString("currentCPUTime");
+ JSObjectRef currentCPUTimeFunction = JSObjectMakeFunctionWithCallback(context, currentCPUTimeStr, currentCPUTime_callAsFunction);
+ JSObjectSetProperty(context, globalObject, currentCPUTimeStr, currentCPUTimeFunction, kJSPropertyAttributeNone, NULL);
+ JSStringRelease(currentCPUTimeStr);
+
+ /* Test script timeout: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, shouldTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ exception = NULL;
+ shouldTerminateCallbackWasCalled = false;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+
+ if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
+ printf("PASS: script timed out as expected.\n");
+ else {
+ if (!((endTime - startTime) < .150f))
+ printf("FAIL: script did not timed out as expected.\n");
+ if (!shouldTerminateCallbackWasCalled)
+ printf("FAIL: script timeout callback was not called.\n");
+ failed = true;
+ }
+
+ if (!exception) {
+ printf("FAIL: TerminatedExecutionException was not thrown.\n");
+ failed = true;
+ }
+ }
+
+ /* Test the script timeout's TerminatedExecutionException should NOT be catchable: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, shouldTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ exception = NULL;
+ shouldTerminateCallbackWasCalled = false;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+
+ if (((endTime - startTime) >= .150f) || !shouldTerminateCallbackWasCalled) {
+ if (!((endTime - startTime) < .150f))
+ printf("FAIL: script did not timed out as expected.\n");
+ if (!shouldTerminateCallbackWasCalled)
+ printf("FAIL: script timeout callback was not called.\n");
+ failed = true;
+ }
+
+ if (exception)
+ printf("PASS: TerminatedExecutionException was not catchable as expected.\n");
+ else {
+ printf("FAIL: TerminatedExecutionException was caught.\n");
+ failed = true;
+ }
+ }
+
+ /* Test script timeout with no callback: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, 0, 0);
+ {
+ const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ exception = NULL;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+
+ if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
+ printf("PASS: script timed out as expected when no callback is specified.\n");
+ else {
+ if (!((endTime - startTime) < .150f))
+ printf("FAIL: script did not timed out as expected when no callback is specified.\n");
+ failed = true;
+ }
+
+ if (!exception) {
+ printf("FAIL: TerminatedExecutionException was not thrown.\n");
+ failed = true;
+ }
+ }
+
+ /* Test script timeout cancellation: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, cancelTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ exception = NULL;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+
+ if (((endTime - startTime) >= .150f) && cancelTerminateCallbackWasCalled && !exception)
+ printf("PASS: script timeout was cancelled as expected.\n");
+ else {
+ if (((endTime - startTime) < .150) || exception)
+ printf("FAIL: script timeout was not cancelled.\n");
+ if (!cancelTerminateCallbackWasCalled)
+ printf("FAIL: script timeout callback was not called.\n");
+ failed = true;
+ }
+
+ if (exception) {
+ printf("FAIL: Unexpected TerminatedExecutionException thrown.\n");
+ failed = true;
+ }
+ }
+
+ /* Test script timeout extension: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 0.100f, extendTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ double deltaTime;
+ exception = NULL;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+ deltaTime = endTime - startTime;
+
+ if ((deltaTime >= .300f) && (deltaTime < .500f) && (extendTerminateCallbackCalled == 2) && exception)
+ printf("PASS: script timeout was extended as expected.\n");
+ else {
+ if (deltaTime < .200f)
+ printf("FAIL: script timeout was not extended as expected.\n");
+ else if (deltaTime >= .500f)
+ printf("FAIL: script did not timeout.\n");
+
+ if (extendTerminateCallbackCalled < 1)
+ printf("FAIL: script timeout callback was not called.\n");
+ if (extendTerminateCallbackCalled < 2)
+ printf("FAIL: script timeout callback was not called after timeout extension.\n");
+
+ if (!exception)
+ printf("FAIL: TerminatedExecutionException was not thrown during timeout extension test.\n");
+
+ failed = true;
+ }
+ }
+#endif /* PLATFORM(MAC) || PLATFORM(IOS) */
+
// Clear out local variables pointing at JSObjectRefs to allow their values to be collected
function = NULL;
v = NULL;
diff --git a/Source/JavaScriptCore/API/tests/testapi.js b/Source/JavaScriptCore/API/tests/testapi.js
index 28fa54433..47c20a830 100644
--- a/Source/JavaScriptCore/API/tests/testapi.js
+++ b/Source/JavaScriptCore/API/tests/testapi.js
@@ -262,6 +262,10 @@ shouldBe("PropertyCatchalls.x", 4);
for (var i = 0; i < 6; ++i)
var x = PropertyCatchalls.x;
shouldBe("x", null);
+var make_throw = 'make_throw';
+shouldThrow("PropertyCatchalls[make_throw]=1");
+make_throw = 0;
+shouldThrow("PropertyCatchalls[make_throw]=1");
for (var i = 0; i < 10; ++i) {
for (var p in PropertyCatchalls) {
diff --git a/Source/JavaScriptCore/API/tests/testapi.mm b/Source/JavaScriptCore/API/tests/testapi.mm
new file mode 100644
index 000000000..defb46e70
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapi.mm
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <JavaScriptCore/JavaScriptCore.h>
+
+extern "C" void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+
+extern "C" bool _Block_has_signature(id);
+extern "C" const char * _Block_signature(id);
+
+extern int failed;
+extern "C" void testObjectiveCAPI(void);
+
+#if JSC_OBJC_API_ENABLED
+
+@protocol ParentObject <JSExport>
+@end
+
+@interface ParentObject : NSObject<ParentObject>
++ (NSString *)parentTest;
+@end
+
+@implementation ParentObject
++ (NSString *)parentTest
+{
+ return [self description];
+}
+@end
+
+@protocol TestObject <JSExport>
+@property int variable;
+@property (readonly) int six;
+@property CGPoint point;
++ (NSString *)classTest;
++ (NSString *)parentTest;
+- (NSString *)getString;
+JSExportAs(testArgumentTypes,
+- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
+);
+- (void)callback:(JSValue *)function;
+- (void)bogusCallback:(void(^)(int))function;
+@end
+
+@interface TestObject : ParentObject <TestObject>
+@property int six;
++ (id)testObject;
+@end
+
+@implementation TestObject
+@synthesize variable;
+@synthesize six;
+@synthesize point;
++ (id)testObject
+{
+ return [[TestObject alloc] init];
+}
++ (NSString *)classTest
+{
+ return @"classTest - okay";
+}
+- (NSString *)getString
+{
+ return @"42";
+}
+- (NSString *)testArgumentTypesWithInt:(int)i double:(double)d boolean:(BOOL)b string:(NSString *)s number:(NSNumber *)n array:(NSArray *)a dictionary:(NSDictionary *)o
+{
+ return [NSString stringWithFormat:@"%d,%g,%d,%@,%d,%@,%@", i, d, b==YES?true:false,s,[n intValue],a[1],o[@"x"]];
+}
+- (void)callback:(JSValue *)function
+{
+ [function callWithArguments:[NSArray arrayWithObject:[NSNumber numberWithInt:42]]];
+}
+- (void)bogusCallback:(void(^)(int))function
+{
+ function(42);
+}
+@end
+
+bool testXYZTested = false;
+
+@protocol TextXYZ <JSExport>
+@property int x;
+@property (readonly) int y;
+@property (assign) JSValue *onclick;
+@property (assign) JSValue *weakOnclick;
+- (void)test:(NSString *)message;
+@end
+
+@interface TextXYZ : NSObject <TextXYZ>
+@property int x;
+@property int y;
+@property int z;
+- (void)click;
+@end
+
+@implementation TextXYZ {
+ JSManagedValue *m_weakOnclickHandler;
+ JSManagedValue *m_onclickHandler;
+}
+@synthesize x;
+@synthesize y;
+@synthesize z;
+- (void)test:(NSString *)message
+{
+ testXYZTested = [message isEqual:@"test"] && x == 13 & y == 4 && z == 5;
+}
+- (void)setWeakOnclick:(JSValue *)value
+{
+ m_weakOnclickHandler = [JSManagedValue managedValueWithValue:value];
+}
+
+- (void)setOnclick:(JSValue *)value
+{
+ m_onclickHandler = [JSManagedValue managedValueWithValue:value];
+ [value.context.virtualMachine addManagedReference:m_onclickHandler withOwner:self];
+}
+- (JSValue *)weakOnclick
+{
+ return [m_weakOnclickHandler value];
+}
+- (JSValue *)onclick
+{
+ return [m_onclickHandler value];
+}
+- (void)click
+{
+ if (!m_onclickHandler)
+ return;
+
+ JSValue *function = [m_onclickHandler value];
+ [function callWithArguments:[NSArray array]];
+}
+- (void)dealloc
+{
+ [[m_onclickHandler value].context.virtualMachine removeManagedReference:m_onclickHandler withOwner:self];
+}
+@end
+
+@class TinyDOMNode;
+
+@protocol TinyDOMNode <JSExport>
+- (void)appendChild:(TinyDOMNode *)child;
+- (NSUInteger)numberOfChildren;
+- (TinyDOMNode *)childAtIndex:(NSUInteger)index;
+- (void)removeChildAtIndex:(NSUInteger)index;
+@end
+
+@interface TinyDOMNode : NSObject<TinyDOMNode>
++ (JSVirtualMachine *)sharedVirtualMachine;
++ (void)clearSharedVirtualMachine;
+@end
+
+@implementation TinyDOMNode {
+ NSMutableArray *m_children;
+}
+
+static JSVirtualMachine *sharedInstance = nil;
+
++ (JSVirtualMachine *)sharedVirtualMachine
+{
+ if (!sharedInstance)
+ sharedInstance = [[JSVirtualMachine alloc] init];
+ return sharedInstance;
+}
+
++ (void)clearSharedVirtualMachine
+{
+ sharedInstance = nil;
+}
+
+- (id)init
+{
+ self = [super init];
+ if (!self)
+ return nil;
+
+ m_children = [[NSMutableArray alloc] initWithCapacity:0];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ NSEnumerator *enumerator = [m_children objectEnumerator];
+ id nextChild;
+ while ((nextChild = [enumerator nextObject]))
+ [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
+
+#if !__has_feature(objc_arc)
+ [super dealloc];
+#endif
+}
+
+- (void)appendChild:(TinyDOMNode *)child
+{
+ [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
+ [m_children addObject:child];
+}
+
+- (NSUInteger)numberOfChildren
+{
+ return [m_children count];
+}
+
+- (TinyDOMNode *)childAtIndex:(NSUInteger)index
+{
+ if (index >= [m_children count])
+ return nil;
+ return [m_children objectAtIndex:index];
+}
+
+- (void)removeChildAtIndex:(NSUInteger)index
+{
+ if (index >= [m_children count])
+ return;
+ [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
+ [m_children removeObjectAtIndex:index];
+}
+
+@end
+
+static void checkResult(NSString *description, bool passed)
+{
+ NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
+ if (!passed)
+ failed = 1;
+}
+
+static bool blockSignatureContainsClass()
+{
+ static bool containsClass = ^{
+ id block = ^(NSString *string){ return string; };
+ return _Block_has_signature(block) && strstr(_Block_signature(block), "NSString");
+ }();
+ return containsClass;
+}
+
+void testObjectiveCAPI()
+{
+ NSLog(@"Testing Objective-C API");
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue *result = [context evaluateScript:@"2 + 2"];
+ checkResult(@"2 + 2", [result isNumber] && [result toInt32] == 4);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ NSString *result = [NSString stringWithFormat:@"Two plus two is %@", [context evaluateScript:@"2 + 2"]];
+ checkResult(@"stringWithFormat", [result isEqual:@"Two plus two is 4"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"message"] = @"Hello";
+ JSValue *result = [context evaluateScript:@"message + ', World!'"];
+ checkResult(@"Hello, World!", [result isString] && [result isEqualToObject:@"Hello, World!"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue *result = [context evaluateScript:@"({ x:42 })"];
+ checkResult(@"({ x:42 })", [result isObject] && [result[@"x"] isEqualToObject:@42]);
+ id obj = [result toObject];
+ checkResult(@"Check dictionary literal", [obj isKindOfClass:[NSDictionary class]]);
+ id num = (NSDictionary *)obj[@"x"];
+ checkResult(@"Check numeric literal", [num isKindOfClass:[NSNumber class]]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ __block int result;
+ context[@"blockCallback"] = ^(int value){
+ result = value;
+ };
+ [context evaluateScript:@"blockCallback(42)"];
+ checkResult(@"blockCallback", result == 42);
+ }
+
+ if (blockSignatureContainsClass()) {
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ __block bool result = false;
+ context[@"blockCallback"] = ^(NSString *value){
+ result = [@"42" isEqualToString:value] == YES;
+ };
+ [context evaluateScript:@"blockCallback(42)"];
+ checkResult(@"blockCallback(NSString *)", result);
+ }
+ } else
+ NSLog(@"Skipping 'blockCallback(NSString *)' test case");
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ checkResult(@"!context.exception", !context.exception);
+ [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
+ checkResult(@"context.exception", context.exception);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ __block bool caught = false;
+ context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
+ (void)context;
+ (void)exception;
+ caught = true;
+ };
+ [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
+ checkResult(@"JSContext.exceptionHandler", caught);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"callback"] = ^{
+ JSContext *context = [JSContext currentContext];
+ context.exception = [JSValue valueWithNewErrorFromMessage:@"Something went wrong." inContext:context];
+ };
+ JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
+ checkResult(@"Explicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
+ checkResult(@"Explicit throw in callback - not thrown to Objective-C", !context.exception);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"callback"] = ^{
+ JSContext *context = [JSContext currentContext];
+ [context evaluateScript:@"!@#$%^&*() THIS IS NOT VALID JAVASCRIPT SYNTAX !@#$%^&*()"];
+ };
+ JSValue *result = [context evaluateScript:@"var result; try { callback(); } catch (e) { result = 'Caught exception'; }"];
+ checkResult(@"Implicit throw in callback - was caught by JavaScript", [result isEqualToObject:@"Caught exception"]);
+ checkResult(@"Implicit throw in callback - not thrown to Objective-C", !context.exception);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ [context evaluateScript:
+ @"function sum(array) { \
+ var result = 0; \
+ for (var i in array) \
+ result += array[i]; \
+ return result; \
+ }"];
+ JSValue *array = [JSValue valueWithObject:@[@13, @2, @7] inContext:context];
+ JSValue *sumFunction = context[@"sum"];
+ JSValue *result = [sumFunction callWithArguments:@[ array ]];
+ checkResult(@"sum([13, 2, 7])", [result toInt32] == 22);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue *mulAddFunction = [context evaluateScript:
+ @"(function(array, object) { \
+ var result = []; \
+ for (var i in array) \
+ result.push(array[i] * object.x + object.y); \
+ return result; \
+ })"];
+ JSValue *result = [mulAddFunction callWithArguments:@[ @[ @2, @4, @8 ], @{ @"x":@0.5, @"y":@42 } ]];
+ checkResult(@"mulAddFunction", [result isObject] && [[result toString] isEqual:@"43,44,46"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue *array = [JSValue valueWithNewArrayInContext:context];
+ checkResult(@"arrayLengthEmpty", [[array[@"length"] toNumber] unsignedIntegerValue] == 0);
+ JSValue *value1 = [JSValue valueWithInt32:42 inContext:context];
+ JSValue *value2 = [JSValue valueWithInt32:24 inContext:context];
+ NSUInteger lowIndex = 5;
+ NSUInteger maxLength = UINT_MAX;
+
+ [array setValue:value1 atIndex:lowIndex];
+ checkResult(@"array.length after put to low index", [[array[@"length"] toNumber] unsignedIntegerValue] == (lowIndex + 1));
+
+ [array setValue:value1 atIndex:(maxLength - 1)];
+ checkResult(@"array.length after put to maxLength - 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
+
+ [array setValue:value2 atIndex:maxLength];
+ checkResult(@"array.length after put to maxLength", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
+
+ [array setValue:value2 atIndex:(maxLength + 1)];
+ checkResult(@"array.length after put to maxLength + 1", [[array[@"length"] toNumber] unsignedIntegerValue] == maxLength);
+
+ checkResult(@"valueAtIndex:0 is undefined", [[array valueAtIndex:0] isUndefined]);
+ checkResult(@"valueAtIndex:lowIndex", [[array valueAtIndex:lowIndex] toInt32] == 42);
+ checkResult(@"valueAtIndex:maxLength - 1", [[array valueAtIndex:(maxLength - 1)] toInt32] == 42);
+ checkResult(@"valueAtIndex:maxLength", [[array valueAtIndex:maxLength] toInt32] == 24);
+ checkResult(@"valueAtIndex:maxLength + 1", [[array valueAtIndex:(maxLength + 1)] toInt32] == 24);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue *object = [JSValue valueWithNewObjectInContext:context];
+
+ object[@"point"] = @{ @"x":@1, @"y":@2 };
+ object[@"point"][@"x"] = @3;
+ CGPoint point = [object[@"point"] toPoint];
+ checkResult(@"toPoint", point.x == 3 && point.y == 2);
+
+ object[@{ @"toString":^{ return @"foo"; } }] = @"bar";
+ checkResult(@"toString in object literal used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
+
+ object[[@"foobar" substringToIndex:3]] = @"bar";
+ checkResult(@"substring used as subscript", [[object[@"foo"] toString] isEqual:@"bar"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TextXYZ *testXYZ = [[TextXYZ alloc] init];
+ context[@"testXYZ"] = testXYZ;
+ testXYZ.x = 3;
+ testXYZ.y = 4;
+ testXYZ.z = 5;
+ [context evaluateScript:@"testXYZ.x = 13; testXYZ.y = 14;"];
+ [context evaluateScript:@"testXYZ.test('test')"];
+ checkResult(@"TextXYZ - testXYZTested", testXYZTested);
+ JSValue *result = [context evaluateScript:@"testXYZ.x + ',' + testXYZ.y + ',' + testXYZ.z"];
+ checkResult(@"TextXYZ - result", [result isEqualToObject:@"13,4,undefined"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ [context[@"Object"][@"prototype"] defineProperty:@"getterProperty" descriptor:@{
+ JSPropertyDescriptorGetKey:^{
+ return [JSContext currentThis][@"x"];
+ }
+ }];
+ JSValue *object = [JSValue valueWithObject:@{ @"x":@101 } inContext:context];
+ int result = [object [@"getterProperty"] toInt32];
+ checkResult(@"getterProperty", result == 101);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"concatenate"] = ^{
+ NSArray *arguments = [JSContext currentArguments];
+ if (![arguments count])
+ return @"";
+ NSString *message = [arguments[0] description];
+ for (NSUInteger index = 1; index < [arguments count]; ++index)
+ message = [NSString stringWithFormat:@"%@ %@", message, arguments[index]];
+ return message;
+ };
+ JSValue *result = [context evaluateScript:@"concatenate('Hello,', 'World!')"];
+ checkResult(@"concatenate", [result isEqualToObject:@"Hello, World!"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"foo"] = @YES;
+ checkResult(@"@YES is boolean", [context[@"foo"] isBoolean]);
+ JSValue *result = [context evaluateScript:@"typeof foo"];
+ checkResult(@"@YES is boolean", [result isEqualToObject:@"boolean"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"String(testObject)"];
+ checkResult(@"String(testObject)", [result isEqualToObject:@"[object TestObject]"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"String(testObject.__proto__)"];
+ checkResult(@"String(testObject.__proto__)", [result isEqualToObject:@"[object TestObjectPrototype]"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"TestObject"] = [TestObject class];
+ JSValue *result = [context evaluateScript:@"String(TestObject)"];
+ checkResult(@"String(TestObject)", [result isEqualToObject:@"[object TestObjectConstructor]"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue* value = [JSValue valueWithObject:[TestObject class] inContext:context];
+ checkResult(@"[value toObject] == [TestObject class]", [value toObject] == [TestObject class]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"TestObject"] = [TestObject class];
+ JSValue *result = [context evaluateScript:@"TestObject.parentTest()"];
+ checkResult(@"TestObject.parentTest()", [result isEqualToObject:@"TestObject"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObjectA"] = testObject;
+ context[@"testObjectB"] = testObject;
+ JSValue *result = [context evaluateScript:@"testObjectA == testObjectB"];
+ checkResult(@"testObjectA == testObjectB", [result isBoolean] && [result toBool]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ testObject.point = (CGPoint){3,4};
+ JSValue *result = [context evaluateScript:@"var result = JSON.stringify(testObject.point); testObject.point = {x:12,y:14}; result"];
+ checkResult(@"testObject.point - result", [result isEqualToObject:@"{\"x\":3,\"y\":4}"]);
+ checkResult(@"testObject.point - {x:12,y:14}", testObject.point.x == 12 && testObject.point.y == 14);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ testObject.six = 6;
+ context[@"testObject"] = testObject;
+ context[@"mul"] = ^(int x, int y){ return x * y; };
+ JSValue *result = [context evaluateScript:@"mul(testObject.six, 7)"];
+ checkResult(@"mul(testObject.six, 7)", [result isNumber] && [result toInt32] == 42);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ context[@"testObject"][@"variable"] = @4;
+ [context evaluateScript:@"++testObject.variable"];
+ checkResult(@"++testObject.variable", testObject.variable == 5);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"point"] = @{ @"x":@6, @"y":@7 };
+ JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
+ checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"point"] = @{ @"x":@6, @"y":@7 };
+ JSValue *result = [context evaluateScript:@"point.x + ',' + point.y"];
+ checkResult(@"point.x + ',' + point.y", [result isEqualToObject:@"6,7"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"testObject.getString()"];
+ checkResult(@"testObject.getString()", [result isString] && [result toInt32] == 42);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"testObject.testArgumentTypes(101,0.5,true,'foo',666,[false,'bar',false],{x:'baz'})"];
+ checkResult(@"testObject.testArgumentTypes", [result isEqualToObject:@"101,0.5,1,foo,666,bar,baz"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"testObject.getString.call(testObject)"];
+ checkResult(@"testObject.getString.call(testObject)", [result isString] && [result toInt32] == 42);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ checkResult(@"testObject.getString.call({}) pre", !context.exception);
+ [context evaluateScript:@"testObject.getString.call({})"];
+ checkResult(@"testObject.getString.call({}) post", context.exception);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject* testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"var result = 0; testObject.callback(function(x){ result = x; }); result"];
+ checkResult(@"testObject.callback", [result isNumber] && [result toInt32] == 42);
+ result = [context evaluateScript:@"testObject.bogusCallback"];
+ checkResult(@"testObject.bogusCallback == undefined", [result isUndefined]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject *testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ JSValue *result = [context evaluateScript:@"Function.prototype.toString.call(testObject.callback)"];
+ checkResult(@"Function.prototype.toString", !context.exception && ![result isUndefined]);
+ }
+
+ @autoreleasepool {
+ JSContext *context1 = [[JSContext alloc] init];
+ JSContext *context2 = [[JSContext alloc] initWithVirtualMachine:context1.virtualMachine];
+ JSValue *value = [JSValue valueWithDouble:42 inContext:context2];
+ context1[@"passValueBetweenContexts"] = value;
+ JSValue *result = [context1 evaluateScript:@"passValueBetweenContexts"];
+ checkResult(@"[value isEqualToObject:result]", [value isEqualToObject:result]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"handleTheDictionary"] = ^(NSDictionary *dict) {
+ NSDictionary *expectedDict = @{
+ @"foo" : [NSNumber numberWithInt:1],
+ @"bar" : @{
+ @"baz": [NSNumber numberWithInt:2]
+ }
+ };
+ checkResult(@"recursively convert nested dictionaries", [dict isEqualToDictionary:expectedDict]);
+ };
+ [context evaluateScript:@"var myDict = { \
+ 'foo': 1, \
+ 'bar': {'baz': 2} \
+ }; \
+ handleTheDictionary(myDict);"];
+
+ context[@"handleTheArray"] = ^(NSArray *array) {
+ NSArray *expectedArray = @[@"foo", @"bar", @[@"baz"]];
+ checkResult(@"recursively convert nested arrays", [array isEqualToArray:expectedArray]);
+ };
+ [context evaluateScript:@"var myArray = ['foo', 'bar', ['baz']]; handleTheArray(myArray);"];
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject *testObject = [TestObject testObject];
+ @autoreleasepool {
+ context[@"testObject"] = testObject;
+ [context evaluateScript:@"var constructor = Object.getPrototypeOf(testObject).constructor; constructor.prototype = undefined;"];
+ [context evaluateScript:@"testObject = undefined"];
+ }
+
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+ @autoreleasepool {
+ context[@"testObject"] = testObject;
+ }
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TextXYZ *testXYZ = [[TextXYZ alloc] init];
+
+ @autoreleasepool {
+ context[@"testXYZ"] = testXYZ;
+
+ [context evaluateScript:@" \
+ didClick = false; \
+ testXYZ.onclick = function() { \
+ didClick = true; \
+ }; \
+ \
+ testXYZ.weakOnclick = function() { \
+ return 'foo'; \
+ }; \
+ "];
+ }
+
+ @autoreleasepool {
+ [testXYZ click];
+ JSValue *result = [context evaluateScript:@"didClick"];
+ checkResult(@"Event handler onclick", [result toBool]);
+ }
+
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+ @autoreleasepool {
+ JSValue *result = [context evaluateScript:@"testXYZ.onclick"];
+ checkResult(@"onclick still around after GC", !([result isNull] || [result isUndefined]));
+ }
+
+
+ @autoreleasepool {
+ JSValue *result = [context evaluateScript:@"testXYZ.weakOnclick"];
+ checkResult(@"weakOnclick not around after GC", [result isNull] || [result isUndefined]);
+ }
+
+ @autoreleasepool {
+ [context evaluateScript:@" \
+ didClick = false; \
+ testXYZ = null; \
+ "];
+ }
+
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+ @autoreleasepool {
+ [testXYZ click];
+ JSValue *result = [context evaluateScript:@"didClick"];
+ checkResult(@"Event handler onclick doesn't fire", ![result toBool]);
+ }
+ }
+
+ @autoreleasepool {
+ JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
+ TestObject *testObject = [TestObject testObject];
+ JSManagedValue *weakValue;
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
+ context[@"testObject"] = testObject;
+ weakValue = [[JSManagedValue alloc] initWithValue:context[@"testObject"]];
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
+ context[@"testObject"] = testObject;
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+ checkResult(@"weak value == nil", ![weakValue value]);
+ checkResult(@"root is still alive", ![context[@"testObject"] isUndefined]);
+ }
+ }
+
+ @autoreleasepool {
+ JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
+ JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
+ TinyDOMNode *root = [[TinyDOMNode alloc] init];
+ TinyDOMNode *lastNode = root;
+ for (NSUInteger i = 0; i < 3; i++) {
+ TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
+ [lastNode appendChild:newNode];
+ lastNode = newNode;
+ }
+
+ @autoreleasepool {
+ context[@"root"] = root;
+ context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
+ TinyDOMNode *lastNode = nil;
+ while (head) {
+ lastNode = head;
+ head = [lastNode childAtIndex:0];
+ }
+ return lastNode;
+ };
+ [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
+ }
+
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+ JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
+ checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
+
+ [TinyDOMNode clearSharedVirtualMachine];
+ }
+
+ @autoreleasepool {
+ JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
+ JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
+ TinyDOMNode *root = [[TinyDOMNode alloc] init];
+ TinyDOMNode *lastNode = root;
+ for (NSUInteger i = 0; i < 3; i++) {
+ TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
+ [lastNode appendChild:newNode];
+ lastNode = newNode;
+ }
+
+ @autoreleasepool {
+ context[@"root"] = root;
+ context[@"getLastNodeInChain"] = ^(TinyDOMNode *head){
+ TinyDOMNode *lastNode = nil;
+ while (head) {
+ lastNode = head;
+ head = [lastNode childAtIndex:0];
+ }
+ return lastNode;
+ };
+ [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty = 42;"];
+
+ [root appendChild:[root childAtIndex:0]];
+ [root removeChildAtIndex:0];
+ }
+
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+ JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
+ checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
+
+ [TinyDOMNode clearSharedVirtualMachine];
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ JSValue *o = [JSValue valueWithNewObjectInContext:context];
+ o[@"foo"] = @"foo";
+ JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+
+ checkResult(@"JSValue correctly protected its internal value", [[o[@"foo"] toString] isEqualToString:@"foo"]);
+ }
+
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ TestObject *testObject = [TestObject testObject];
+ context[@"testObject"] = testObject;
+ [context evaluateScript:@"testObject.__lookupGetter__('variable').call({})"];
+ checkResult(@"Make sure we throw an exception when calling getter on incorrect |this|", context.exception);
+ }
+
+ @autoreleasepool {
+ TestObject *testObject = [TestObject testObject];
+ JSManagedValue *managedTestObject;
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+ context[@"testObject"] = testObject;
+ managedTestObject = [JSManagedValue managedValueWithValue:context[@"testObject"]];
+ [context.virtualMachine addManagedReference:managedTestObject withOwner:testObject];
+ }
+ }
+}
+
+#else
+
+void testObjectiveCAPI()
+{
+}
+
+#endif