Diff
Modified: trunk/Source/_javascript_Core/API/JSContext.mm (236371 => 236372)
--- trunk/Source/_javascript_Core/API/JSContext.mm 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/JSContext.mm 2018-09-21 23:38:25 UTC (rev 236372)
@@ -158,7 +158,8 @@
{
Thread& thread = Thread::current();
CallbackData *entry = (CallbackData *)thread.m_apiData;
- if (!entry)
+ // calleeValue may be null if we are initializing a promise.
+ if (!entry || !entry->calleeValue)
return nil;
return [JSValue valueWithJSValueRef:entry->calleeValue inContext:[JSContext currentContext]];
}
Modified: trunk/Source/_javascript_Core/API/JSObjectRef.cpp (236371 => 236372)
--- trunk/Source/_javascript_Core/API/JSObjectRef.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/JSObjectRef.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -46,6 +46,9 @@
#include "JSFunction.h"
#include "JSGlobalObject.h"
#include "JSObject.h"
+#include "JSPromise.h"
+#include "JSPromiseDeferred.h"
+#include "JSRetainPtr.h"
#include "JSString.h"
#include "JSValueRef.h"
#include "ObjectConstructor.h"
@@ -274,6 +277,30 @@
return toRef(result);
}
+JSObjectRef JSObjectMakeDeferredPromise(JSContextRef ctx, JSObjectRef* resolve, JSObjectRef* reject, JSValueRef* exception)
+{
+ if (!ctx) {
+ ASSERT_NOT_REACHED();
+ return nullptr;
+ }
+
+ ExecState* exec = toJS(ctx);
+ VM& vm = exec->vm();
+ JSLockHolder locker(exec);
+ auto scope = DECLARE_CATCH_SCOPE(vm);
+
+ auto* globalObject = exec->lexicalGlobalObject();
+ JSPromiseDeferred::DeferredData data = "" globalObject, globalObject->promiseConstructor());
+ if (handleExceptionIfNeeded(scope, exec, exception) == ExceptionStatus::DidThrow)
+ return nullptr;
+
+ if (resolve)
+ *resolve = toRef(data.resolve);
+ if (reject)
+ *reject = toRef(data.reject);
+ return toRef(data.promise);
+}
+
JSValueRef JSObjectGetPrototype(JSContextRef ctx, JSObjectRef object)
{
if (!ctx) {
Modified: trunk/Source/_javascript_Core/API/JSObjectRefPrivate.h (236371 => 236372)
--- trunk/Source/_javascript_Core/API/JSObjectRefPrivate.h 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/JSObjectRefPrivate.h 2018-09-21 23:38:25 UTC (rev 236372)
@@ -70,6 +70,17 @@
JS_EXPORT JSObjectRef JSObjectGetProxyTarget(JSObjectRef);
JS_EXPORT JSGlobalContextRef JSObjectGetGlobalContext(JSObjectRef object);
+
+/*!
+ @function
+ @abstract Creates a _javascript_ promise object by invoking the provided executor.
+ @param ctx The execution context to use.
+ @param resolve A pointer to a JSObjectRef in which to store the resolve function for the new promise. Pass NULL if you do not care to store the resolve callback.
+ @param reject A pointer to a JSObjectRef in which to store the reject function for the new promise. Pass NULL if you do not care to store the reject callback.
+ @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 A JSObject that is a promise or NULL if an exception occurred.
+ */
+JS_EXPORT JSObjectRef JSObjectMakeDeferredPromise(JSContextRef ctx, JSObjectRef* resolve, JSObjectRef* reject, JSValueRef* exception) JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
#ifdef __cplusplus
}
Modified: trunk/Source/_javascript_Core/API/JSValue.mm (236371 => 236372)
--- trunk/Source/_javascript_Core/API/JSValue.mm 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/JSValue.mm 2018-09-21 23:38:25 UTC (rev 236372)
@@ -31,8 +31,10 @@
#import "Exception.h"
#import "_javascript_Core.h"
#import "JSContextInternal.h"
+#import "JSObjectRefPrivate.h"
#import "JSVirtualMachineInternal.h"
#import "JSValueInternal.h"
+#import "JSValuePrivate.h"
#import "JSWrapperMap.h"
#import "ObjcRuntimeExtras.h"
#import "JSCInlines.h"
@@ -153,6 +155,48 @@
return [JSValue valueWithJSValueRef:JSValueMakeSymbol([context JSGlobalContextRef], string.get()) inContext:context];
}
++ (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *, JSValue *))executor
+{
+ JSObjectRef resolve;
+ JSObjectRef reject;
+ JSValueRef exception = nullptr;
+ JSObjectRef promise = JSObjectMakeDeferredPromise([context JSGlobalContextRef], &resolve, &reject, &exception);
+ if (exception) {
+ [context notifyException:exception];
+ return [JSValue valueWithUndefinedInContext:context];
+ }
+
+ JSValue *result = [JSValue valueWithJSValueRef:promise inContext:context];
+ JSValue *rejection = [JSValue valueWithJSValueRef:reject inContext:context];
+ CallbackData callbackData;
+ const size_t argumentCount = 2;
+ JSValueRef arguments[argumentCount];
+ arguments[0] = resolve;
+ arguments[1] = reject;
+
+ [context beginCallbackWithData:&callbackData calleeValue:nullptr thisValue:promise argumentCount:argumentCount arguments:arguments];
+ executor([JSValue valueWithJSValueRef:resolve inContext:context], rejection);
+ if (context.exception)
+ [rejection callWithArguments:@[context.exception]];
+ [context endCallbackWithData:&callbackData];
+
+ return result;
+}
+
++ (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context
+{
+ return [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *resolve, JSValue *) {
+ [resolve callWithArguments:@[result]];
+ }];
+}
+
++ (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context
+{
+ return [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *, JSValue *reject) {
+ [reject callWithArguments:@[reason]];
+ }];
+}
+
- (id)toObject
{
return valueToObject(_context, m_value);
Added: trunk/Source/_javascript_Core/API/JSValuePrivate.h (0 => 236372)
--- trunk/Source/_javascript_Core/API/JSValuePrivate.h (rev 0)
+++ trunk/Source/_javascript_Core/API/JSValuePrivate.h 2018-09-21 23:38:25 UTC (rev 236372)
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#if JSC_OBJC_API_ENABLED
+
+#import <_javascript_Core/_javascript_Core.h>
+
+@interface JSValue(JSPrivate)
+
+/*!
+ @method
+ @abstract Create a new promise object using the provided executor callback.
+ @param callback A callback block invoked while the promise object is
+ being initialized. The resolve and reject parameters are functions that
+ can be called to notify any pending reactions about the state of the
+ new promise object.
+ @param context The JSContext to which the resulting JSValue belongs.
+ @result The JSValue representing a new promise _javascript_ object.
+ @discussion This method is equivalent to calling the Promise constructor in _javascript_.
+ the resolve and reject callbacks each normally take a single value, which they
+ forward to all relevent pending reactions. While inside the executor callback context will act
+ as if it were in any other callback, except calleeFunction will be <code>nil</code>. This also means
+ means the new promise object may be accessed via <code>[context thisValue]</code>.
+ */
++ (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *resolve, JSValue *reject))callback JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
+
+/*!
+ @method
+ @abstract Create a new resolved promise object with the provided value.
+ @param result The result value to be passed to any reactions.
+ @param context The JSContext to which the resulting JSValue belongs.
+ @result The JSValue representing a new promise _javascript_ object.
+ @discussion This method is equivalent to calling <code>[JSValue valueWithNewPromiseFromExecutor:^(JSValue *resolve, JSValue *reject) { [resolve callWithArguments:@[result]]; } inContext:context]</code>
+ */
++ (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
+
+/*!
+ @method
+ @abstract Create a new rejected promise object with the provided value.
+ @param reason The result value to be passed to any reactions.
+ @param context The JSContext to which the resulting JSValue belongs.
+ @result The JSValue representing a new promise _javascript_ object.
+ @discussion This method is equivalent to calling <code>[JSValue valueWithNewPromiseFromExecutor:^(JSValue *resolve, JSValue *reject) { [reject callWithArguments:@[reason]]; } inContext:context]</code>
+ */
++ (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
+
+@end
+
+#endif // JSC_OBJC_API_ENABLED
Modified: trunk/Source/_javascript_Core/API/JSVirtualMachine.mm (236371 => 236372)
--- trunk/Source/_javascript_Core/API/JSVirtualMachine.mm 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/JSVirtualMachine.mm 2018-09-21 23:38:25 UTC (rev 236372)
@@ -32,12 +32,13 @@
#import "APICast.h"
#import "DFGWorklist.h"
#import "JSManagedValueInternal.h"
-#import "JSVirtualMachine.h"
#import "JSVirtualMachineInternal.h"
+#import "JSVirtualMachinePrivate.h"
#import "JSWrapperMap.h"
#import "SigillCrashAnalyzer.h"
#import "SlotVisitorInlines.h"
#import <mutex>
+#import <wtf/BlockPtr.h>
#import <wtf/Lock.h>
#import <wtf/spi/cocoa/NSMapTableSPI.h>
Modified: trunk/Source/_javascript_Core/API/JSVirtualMachinePrivate.h (236371 => 236372)
--- trunk/Source/_javascript_Core/API/JSVirtualMachinePrivate.h 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/JSVirtualMachinePrivate.h 2018-09-21 23:38:25 UTC (rev 236372)
@@ -43,7 +43,7 @@
API may not synchronously free memory.
*/
-- (void)shrinkFootprintWhenIdle NS_AVAILABLE(10_14, 12_0);
+- (void)shrinkFootprintWhenIdle JSC_API_AVAILABLE(macosx(10.14), ios(12.0));
#if ENABLE(DFG_JIT)
@@ -58,7 +58,7 @@
@param numberOfThreads The number of threads the DFG compiler should use going forward
@result The previous number of threads being used by the DFG compiler
*/
-+ (NSUInteger)setNumberOfDFGCompilerThreads:(NSUInteger)numberOfThreads NS_AVAILABLE(10_14, 12_0);
++ (NSUInteger)setNumberOfDFGCompilerThreads:(NSUInteger)numberOfThreads JSC_API_AVAILABLE(macosx(10.14), ios(12.0));
/*!
@method
@@ -71,7 +71,7 @@
@param numberOfThreads The number of threads the FTL compiler should use going forward
@result The previous number of threads being used by the FTL compiler
*/
-+ (NSUInteger)setNumberOfFTLCompilerThreads:(NSUInteger)numberOfThreads NS_AVAILABLE(10_14, 12_0);
++ (NSUInteger)setNumberOfFTLCompilerThreads:(NSUInteger)numberOfThreads JSC_API_AVAILABLE(macosx(10.14), ios(12.0));
#endif // ENABLE(DFG_JIT)
Modified: trunk/Source/_javascript_Core/API/tests/testapi.c (236371 => 236372)
--- trunk/Source/_javascript_Core/API/tests/testapi.c 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/tests/testapi.c 2018-09-21 23:38:25 UTC (rev 236372)
@@ -70,7 +70,7 @@
void testObjectiveCAPI(void);
#endif
-int testCAPIViaCpp(void);
+int testCAPIViaCpp(const char* filter);
bool assertTrue(bool value, const char* message);
@@ -1380,18 +1380,17 @@
SetErrorMode(0);
#endif
- testCompareAndSwap();
- startMultithreadedMultiVMExecutionTest();
-
#if JSC_OBJC_API_ENABLED
testObjectiveCAPI();
#endif
- RELEASE_ASSERT(!testCAPIViaCpp());
- const char *scriptPath = "testapi.js";
- if (argc > 1) {
- scriptPath = argv[1];
- }
+ const char* filter = argc > 1 ? argv[1] : NULL;
+ RELEASE_ASSERT(!testCAPIViaCpp(filter));
+ if (filter)
+ return 0;
+
+ testCompareAndSwap();
+ startMultithreadedMultiVMExecutionTest();
// Test garbage collection with a fresh context
context = JSGlobalContextCreateInGroup(NULL, NULL);
@@ -1980,6 +1979,7 @@
JSObjectMakeConstructor(context, nullClass, 0);
JSClassRelease(nullClass);
+ const char* scriptPath = "testapi.js";
char* scriptUTF8 = createStringWithContentsOfFile(scriptPath);
if (!scriptUTF8) {
printf("FAIL: Test script could not be loaded.\n");
Modified: trunk/Source/_javascript_Core/API/tests/testapi.cpp (236371 => 236372)
--- trunk/Source/_javascript_Core/API/tests/testapi.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/tests/testapi.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -29,13 +29,15 @@
#include "JSCJSValueInlines.h"
#include "JSObject.h"
+#include <_javascript_Core/JSObjectRefPrivate.h>
#include <_javascript_Core/_javascript_.h>
#include <wtf/DataLog.h>
#include <wtf/Expected.h>
#include <wtf/Noncopyable.h>
+#include <wtf/NumberOfCores.h>
#include <wtf/Vector.h>
-extern "C" int testCAPIViaCpp();
+extern "C" int testCAPIViaCpp(const char* filter);
class APIString {
WTF_MAKE_NONCOPYABLE(APIString);
@@ -83,6 +85,7 @@
}
operator JSGlobalContextRef() { return m_context; }
+ operator JSC::ExecState*() { return toJS(m_context); }
private:
JSGlobalContextRef m_context;
@@ -122,7 +125,19 @@
class TestAPI {
public:
- int run();
+ int run(const char* filter);
+
+ void basicSymbol();
+ void symbolsTypeof();
+ void symbolsGetPropertyForKey();
+ void symbolsSetPropertyForKey();
+ void symbolsHasPropertyForKey();
+ void symbolsDeletePropertyForKey();
+ void promiseResolveTrue();
+ void promiseRejectTrue();
+
+ int failed() const { return m_failed; }
+
private:
template<typename... Strings>
@@ -143,16 +158,10 @@
APIVector<JSObjectRef> interestingObjects();
APIVector<JSValueRef> interestingKeys();
- int failed { 0 };
+ int m_failed { 0 };
APIContext context;
};
-int testCAPIViaCpp()
-{
- TestAPI tester;
- return tester.run();
-}
-
TestAPI::ScriptResult TestAPI::evaluateScript(const char* script, JSObjectRef thisObject)
{
APIString scriptAPIString(script);
@@ -202,7 +211,7 @@
{
if (!condition) {
dataLogLn(messages..., ": FAILED");
- failed++;
+ m_failed++;
} else
dataLogLn(messages..., ": PASSED");
@@ -257,139 +266,273 @@
return result;
}
-int TestAPI::run()
+static const char* isSymbolFunction = "(function isSymbol(symbol) { return typeof(symbol) === 'symbol'; })";
+static const char* getFunction = "(function get(object, key) { return object[key]; })";
+static const char* setFunction = "(function set(object, key, value) { object[key] = value; })";
+
+void TestAPI::basicSymbol()
{
- dataLogLn("Starting C-API tests in C++");
- const char* isSymbolFunction = "(function isSymbol(symbol) { return typeof(symbol) === 'symbol'; })";
- const char* getFunction = "(function get(object, key) { return object[key]; })";
- const char* setFunction = "(function set(object, key, value) { object[key] = value; })";
- const char* hasFunction = "(function has(object, key) { return key in object; })";
- const char* deleteFunction = "(function del(object, key) { return delete object[key]; })";
+ // Can't call Symbol as a constructor since it's not subclassable.
+ auto result = evaluateScript("Symbol('dope');");
+ check(JSValueGetType(context, result.value()) == kJSTypeSymbol, "dope get type is a symbol");
+ check(JSValueIsSymbol(context, result.value()), "dope is a symbol");
+}
- JSC::ExecState* exec = toJS(context);
+void TestAPI::symbolsTypeof()
+{
+ APIString description("dope");
+ JSValueRef symbol = JSValueMakeSymbol(context, description);
+ check(functionReturnsTrue(isSymbolFunction, symbol), "JSValueMakeSymbol makes a symbol value");
+}
- {
- // Can't call Symbol as a constructor since it's not subclassable.
- auto result = evaluateScript("Symbol('dope');");
- check(JSValueGetType(context, result.value()) == kJSTypeSymbol, "dope get type is a symbol");
- check(JSValueIsSymbol(context, result.value()), "dope is a symbol");
- }
+void TestAPI::symbolsGetPropertyForKey()
+{
+ auto objects = interestingObjects();
+ auto keys = interestingKeys();
- {
- APIString description("dope");
- JSValueRef symbol = JSValueMakeSymbol(context, description);
- check(functionReturnsTrue(isSymbolFunction, symbol), "JSValueMakeSymbol makes a symbol value");
+ for (auto& object : objects) {
+ dataLogLn("\nnext object: ", toJS(context, object));
+ for (auto& key : keys) {
+ dataLogLn("Using key: ", toJS(context, key));
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(getFunction, object, key);
+ }, [&] (JSValueRef* exception) {
+ return JSObjectGetPropertyForKey(context, object, key, exception);
+ }, "checking get property keys");
+ }
}
+}
- {
- auto objects = interestingObjects();
- auto keys = interestingKeys();
+void TestAPI::symbolsSetPropertyForKey()
+{
+ auto jsObjects = interestingObjects();
+ auto apiObjects = interestingObjects();
+ auto keys = interestingKeys();
- for (auto& object : objects) {
- dataLogLn("\nnext object: ", toJS(exec, object));
- for (auto& key : keys) {
- dataLogLn("Using key: ", toJS(exec, key));
- checkJSAndAPIMatch(
- [&] {
- return callFunction(getFunction, object, key);
- }, [&] (JSValueRef* exception) {
- return JSObjectGetPropertyForKey(context, object, key, exception);
- }, "checking get property keys");
- }
+ JSValueRef theAnswer = JSValueMakeNumber(context, 42);
+ for (size_t i = 0; i < jsObjects.size(); i++) {
+ for (auto& key : keys) {
+ JSObjectRef jsObject = jsObjects[i];
+ JSObjectRef apiObject = apiObjects[i];
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(setFunction, jsObject, key, theAnswer);
+ } , [&] (JSValueRef* exception) {
+ JSObjectSetPropertyForKey(context, apiObject, key, theAnswer, kJSPropertyAttributeNone, exception);
+ return JSValueMakeUndefined(context);
+ }, "setting property keys to the answer");
+ // Check get is the same on API object.
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(getFunction, apiObject, key);
+ }, [&] (JSValueRef* exception) {
+ return JSObjectGetPropertyForKey(context, apiObject, key, exception);
+ }, "getting property keys from API objects");
+ // Check get is the same on respective objects.
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(getFunction, jsObject, key);
+ }, [&] (JSValueRef* exception) {
+ return JSObjectGetPropertyForKey(context, apiObject, key, exception);
+ }, "getting property keys from respective objects");
}
}
+}
- {
- auto jsObjects = interestingObjects();
- auto apiObjects = interestingObjects();
- auto keys = interestingKeys();
+void TestAPI::symbolsHasPropertyForKey()
+{
+ const char* hasFunction = "(function has(object, key) { return key in object; })";
+ auto objects = interestingObjects();
+ auto keys = interestingKeys();
- JSValueRef theAnswer = JSValueMakeNumber(context, 42);
- for (size_t i = 0; i < jsObjects.size(); i++) {
- for (auto& key : keys) {
- JSObjectRef jsObject = jsObjects[i];
- JSObjectRef apiObject = apiObjects[i];
- checkJSAndAPIMatch(
- [&] {
- return callFunction(setFunction, jsObject, key, theAnswer);
- } , [&] (JSValueRef* exception) {
- JSObjectSetPropertyForKey(context, apiObject, key, theAnswer, kJSPropertyAttributeNone, exception);
- return JSValueMakeUndefined(context);
- }, "setting property keys to the answer");
- // Check get is the same on API object.
- checkJSAndAPIMatch(
- [&] {
- return callFunction(getFunction, apiObject, key);
- }, [&] (JSValueRef* exception) {
- return JSObjectGetPropertyForKey(context, apiObject, key, exception);
- }, "getting property keys from API objects");
- // Check get is the same on respective objects.
- checkJSAndAPIMatch(
- [&] {
- return callFunction(getFunction, jsObject, key);
- }, [&] (JSValueRef* exception) {
- return JSObjectGetPropertyForKey(context, apiObject, key, exception);
- }, "getting property keys from respective objects");
- }
+ JSValueRef theAnswer = JSValueMakeNumber(context, 42);
+ for (auto& object : objects) {
+ dataLogLn("\nNext object: ", toJS(context, object));
+ for (auto& key : keys) {
+ dataLogLn("Using key: ", toJS(context, key));
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(hasFunction, object, key);
+ }, [&] (JSValueRef* exception) {
+ return JSValueMakeBoolean(context, JSObjectHasPropertyForKey(context, object, key, exception));
+ }, "checking has property keys unset");
+
+ check(!!callFunction(setFunction, object, key, theAnswer), "set property to the answer");
+
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(hasFunction, object, key);
+ }, [&] (JSValueRef* exception) {
+ return JSValueMakeBoolean(context, JSObjectHasPropertyForKey(context, object, key, exception));
+ }, "checking has property keys set");
}
}
+}
- {
- auto objects = interestingObjects();
- auto keys = interestingKeys();
- JSValueRef theAnswer = JSValueMakeNumber(context, 42);
- for (auto& object : objects) {
- dataLogLn("\nNext object: ", toJS(exec, object));
- for (auto& key : keys) {
- dataLogLn("Using key: ", toJS(exec, key));
- checkJSAndAPIMatch(
- [&] {
- return callFunction(hasFunction, object, key);
- }, [&] (JSValueRef* exception) {
- return JSValueMakeBoolean(context, JSObjectHasPropertyForKey(context, object, key, exception));
- }, "checking has property keys unset");
+void TestAPI::symbolsDeletePropertyForKey()
+{
+ const char* deleteFunction = "(function del(object, key) { return delete object[key]; })";
+ auto objects = interestingObjects();
+ auto keys = interestingKeys();
- check(!!callFunction(setFunction, object, key, theAnswer), "set property to the answer");
+ JSValueRef theAnswer = JSValueMakeNumber(context, 42);
+ for (auto& object : objects) {
+ dataLogLn("\nNext object: ", toJS(context, object));
+ for (auto& key : keys) {
+ dataLogLn("Using key: ", toJS(context, key));
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(deleteFunction, object, key);
+ }, [&] (JSValueRef* exception) {
+ return JSValueMakeBoolean(context, JSObjectDeletePropertyForKey(context, object, key, exception));
+ }, "checking has property keys unset");
- checkJSAndAPIMatch(
- [&] {
- return callFunction(hasFunction, object, key);
- }, [&] (JSValueRef* exception) {
- return JSValueMakeBoolean(context, JSObjectHasPropertyForKey(context, object, key, exception));
- }, "checking has property keys set");
- }
+ check(!!callFunction(setFunction, object, key, theAnswer), "set property to the answer");
+
+ checkJSAndAPIMatch(
+ [&] {
+ return callFunction(deleteFunction, object, key);
+ }, [&] (JSValueRef* exception) {
+ return JSValueMakeBoolean(context, JSObjectDeletePropertyForKey(context, object, key, exception));
+ }, "checking has property keys set");
}
}
+}
- {
- auto objects = interestingObjects();
- auto keys = interestingKeys();
+void TestAPI::promiseResolveTrue()
+{
+ JSObjectRef resolve;
+ JSObjectRef reject;
+ JSValueRef exception = nullptr;
+ JSObjectRef promise = JSObjectMakeDeferredPromise(context, &resolve, &reject, &exception);
+ check(!exception, "No exception should be thrown creating a deferred promise");
- JSValueRef theAnswer = JSValueMakeNumber(context, 42);
- for (auto& object : objects) {
- dataLogLn("\nNext object: ", toJS(exec, object));
- for (auto& key : keys) {
- dataLogLn("Using key: ", toJS(exec, key));
- checkJSAndAPIMatch(
- [&] {
- return callFunction(deleteFunction, object, key);
- }, [&] (JSValueRef* exception) {
- return JSValueMakeBoolean(context, JSObjectDeletePropertyForKey(context, object, key, exception));
- }, "checking has property keys unset");
+ // Ugh, we don't have any C API that takes blocks... so we do this hack to capture the runner.
+ static TestAPI* tester = this;
+ static bool passedTrueCalled = false;
- check(!!callFunction(setFunction, object, key, theAnswer), "set property to the answer");
+ APIString trueString("passedTrue");
+ auto passedTrue = [](JSContextRef ctx, JSObjectRef, JSObjectRef, size_t argumentCount, const JSValueRef arguments[], JSValueRef*) -> JSValueRef {
+ tester->check(argumentCount && JSValueIsStrictEqual(ctx, arguments[0], JSValueMakeBoolean(ctx, true)), "function should have been called back with true");
+ passedTrueCalled = true;
+ return JSValueMakeUndefined(ctx);
+ };
- checkJSAndAPIMatch(
- [&] {
- return callFunction(deleteFunction, object, key);
- }, [&] (JSValueRef* exception) {
- return JSValueMakeBoolean(context, JSObjectDeletePropertyForKey(context, object, key, exception));
- }, "checking has property keys set");
- }
- }
+ APIString thenString("then");
+ JSValueRef thenFunction = JSObjectGetProperty(context, promise, thenString, &exception);
+ check(!exception && thenFunction && JSValueIsObject(context, thenFunction), "Promise should have a then object property");
+
+ JSValueRef passedTrueFunction = JSObjectMakeFunctionWithCallback(context, trueString, passedTrue);
+ JSObjectCallAsFunction(context, const_cast<JSObjectRef>(thenFunction), promise, 1, &passedTrueFunction, &exception);
+ check(!exception, "No exception should be thrown setting up callback");
+
+ auto trueValue = JSValueMakeBoolean(context, true);
+ JSObjectCallAsFunction(context, resolve, resolve, 1, &trueValue, &exception);
+ check(!exception, "No exception should be thrown resolve promise");
+ check(passedTrueCalled, "then response function should have been called.");
+}
+
+void TestAPI::promiseRejectTrue()
+{
+ JSObjectRef resolve;
+ JSObjectRef reject;
+ JSValueRef exception = nullptr;
+ JSObjectRef promise = JSObjectMakeDeferredPromise(context, &resolve, &reject, &exception);
+ check(!exception, "No exception should be thrown creating a deferred promise");
+
+ // Ugh, we don't have any C API that takes blocks... so we do this hack to capture the runner.
+ static TestAPI* tester = this;
+ static bool passedTrueCalled = false;
+
+ APIString trueString("passedTrue");
+ auto passedTrue = [](JSContextRef ctx, JSObjectRef, JSObjectRef, size_t argumentCount, const JSValueRef arguments[], JSValueRef*) -> JSValueRef {
+ tester->check(argumentCount && JSValueIsStrictEqual(ctx, arguments[0], JSValueMakeBoolean(ctx, true)), "function should have been called back with true");
+ passedTrueCalled = true;
+ return JSValueMakeUndefined(ctx);
+ };
+
+ APIString catchString("catch");
+ JSValueRef catchFunction = JSObjectGetProperty(context, promise, catchString, &exception);
+ check(!exception && catchFunction && JSValueIsObject(context, catchFunction), "Promise should have a then object property");
+
+ JSValueRef passedTrueFunction = JSObjectMakeFunctionWithCallback(context, trueString, passedTrue);
+ JSObjectCallAsFunction(context, const_cast<JSObjectRef>(catchFunction), promise, 1, &passedTrueFunction, &exception);
+ check(!exception, "No exception should be thrown setting up callback");
+
+ auto trueValue = JSValueMakeBoolean(context, true);
+ JSObjectCallAsFunction(context, reject, reject, 1, &trueValue, &exception);
+ check(!exception, "No exception should be thrown resolve promise");
+ check(passedTrueCalled, "then response function should have been called.");
+}
+
+#define RUN(test) do { \
+ if (!shouldRun(#test)) \
+ break; \
+ tasks.append( \
+ createSharedTask<void(TestAPI&)>( \
+ [&] (TestAPI& tester) { \
+ tester.test; \
+ dataLog(#test ": OK!\n"); \
+ })); \
+ } while (false)
+
+int testCAPIViaCpp(const char* filter)
+{
+ dataLogLn("Starting C-API tests in C++");
+
+ Deque<RefPtr<SharedTask<void(TestAPI&)>>> tasks;
+
+#if OS(DARWIN)
+ auto shouldRun = [&] (const char* testName) -> bool {
+ return !filter || !!strcasestr(testName, filter);
+ };
+#else
+ auto shouldRun = [] (const char*) -> bool { return true; };
+#endif
+
+ RUN(basicSymbol());
+ RUN(symbolsTypeof());
+ RUN(symbolsGetPropertyForKey());
+ RUN(symbolsSetPropertyForKey());
+ RUN(symbolsHasPropertyForKey());
+ RUN(symbolsDeletePropertyForKey());
+ RUN(promiseResolveTrue());
+ RUN(promiseRejectTrue());
+
+ if (tasks.isEmpty()) {
+ dataLogLn("Filtered all tests: ERROR");
+ return 1;
}
- dataLogLn("C-API tests in C++ had ", failed, " failures");
- return failed;
+ Lock lock;
+
+ static Atomic<int> failed { 0 };
+ Vector<Ref<Thread>> threads;
+ for (unsigned i = filter ? 1 : WTF::numberOfProcessorCores(); i--;) {
+ threads.append(Thread::create(
+ "Testapi via C++ thread",
+ [&] () {
+ TestAPI tester;
+ for (;;) {
+ RefPtr<SharedTask<void(TestAPI&)>> task;
+ {
+ LockHolder locker(lock);
+ if (tasks.isEmpty())
+ return;
+ task = tasks.takeFirst();
+ }
+
+ task->run(tester);
+ }
+ failed.exchangeAdd(tester.failed());
+ }));
+ }
+
+ for (auto& thread : threads)
+ thread->waitForCompletion();
+
+ dataLogLn("C-API tests in C++ had ", failed.load(), " failures");
+ return failed.load();
}
Modified: trunk/Source/_javascript_Core/API/tests/testapi.mm (236371 => 236372)
--- trunk/Source/_javascript_Core/API/tests/testapi.mm 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/API/tests/testapi.mm 2018-09-21 23:38:25 UTC (rev 236372)
@@ -27,13 +27,13 @@
#import "JSExportMacros.h"
#import <_javascript_Core/_javascript_Core.h>
-#undef NS_AVAILABLE
-#define NS_AVAILABLE(_mac, _ios)
-
#import "CurrentThisInsideBlockGetterTest.h"
#import "DFGWorklist.h"
#import "DateTests.h"
+#import "JSCast.h"
#import "JSExportTests.h"
+#import "JSValuePrivate.h"
+#import "JSVirtualMachineInternal.h"
#import "JSVirtualMachinePrivate.h"
#import "JSWrapperMapTests.h"
#import "Regress141275.h"
@@ -556,8 +556,6 @@
static void testObjectiveCAPIMain()
{
- runJITThreadLimitTests();
-
@autoreleasepool {
JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm];
@@ -1653,11 +1651,182 @@
checkResult(@"Negative number maintained its original value", [[result toString] isEqualToString:@"-1"]);
}
+enum class Resolution {
+ ResolveEager,
+ RejectEager,
+ ResolveLate,
+ RejectLate,
+};
+static void promiseWithExecutor(Resolution resolution)
+{
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ __block JSValue *resolveCallback;
+ __block JSValue *rejectCallback;
+ JSValue *promise = [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *resolve, JSValue *reject) {
+ if (resolution == Resolution::ResolveEager)
+ [resolve callWithArguments:@[@YES]];
+ if (resolution == Resolution::RejectEager)
+ [reject callWithArguments:@[@YES]];
+ resolveCallback = resolve;
+ rejectCallback = reject;
+ }];
+
+ __block bool valueWasResolvedTrue = false;
+ __block bool valueWasRejectedTrue = false;
+ [promise invokeMethod:@"then" withArguments:@[
+ ^(JSValue *value) { valueWasResolvedTrue = [value isBoolean] && [value toBool]; },
+ ^(JSValue *value) { valueWasRejectedTrue = [value isBoolean] && [value toBool]; },
+ ]];
+
+ switch (resolution) {
+ case Resolution::ResolveEager:
+ checkResult(@"ResolveEager should have set resolve early.", valueWasResolvedTrue && !valueWasRejectedTrue);
+ break;
+ case Resolution::RejectEager:
+ checkResult(@"RejectEager should have set reject early.", !valueWasResolvedTrue && valueWasRejectedTrue);
+ break;
+ default:
+ checkResult(@"Resolve/RejectLate should have not have set anything early.", !valueWasResolvedTrue && !valueWasRejectedTrue);
+ break;
+ }
+
+ valueWasResolvedTrue = false;
+ valueWasRejectedTrue = false;
+
+ // Run script to make sure reactions don't happen again
+ [context evaluateScript:@"{ };"];
+
+ if (resolution == Resolution::ResolveLate)
+ [resolveCallback callWithArguments:@[@YES]];
+ if (resolution == Resolution::RejectLate)
+ [rejectCallback callWithArguments:@[@YES]];
+
+ switch (resolution) {
+ case Resolution::ResolveLate:
+ checkResult(@"ResolveLate should have set resolve late.", valueWasResolvedTrue && !valueWasRejectedTrue);
+ break;
+ case Resolution::RejectLate:
+ checkResult(@"RejectLate should have set reject late.", !valueWasResolvedTrue && valueWasRejectedTrue);
+ break;
+ default:
+ checkResult(@"Resolve/RejectEarly should have not have set anything late.", !valueWasResolvedTrue && !valueWasRejectedTrue);
+ break;
+ }
+ }
+}
+
+static void promiseRejectOnJSException()
+{
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ JSValue *promise = [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *, JSValue *) {
+ context.exception = [JSValue valueWithNewErrorFromMessage:@"dope" inContext:context];
+ }];
+ checkResult(@"Exception set in callback should not propagate", !context.exception);
+
+ __block bool reasonWasObject = false;
+ [promise invokeMethod:@"catch" withArguments:@[^(JSValue *reason) { reasonWasObject = [reason isObject]; }]];
+
+ checkResult(@"Setting an exception in executor causes the promise to be rejected", reasonWasObject);
+
+ promise = [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *, JSValue *) {
+ [context evaluateScript:@"throw new Error('dope');"];
+ }];
+ checkResult(@"Exception thrown in callback should not propagate", !context.exception);
+
+ reasonWasObject = false;
+ [promise invokeMethod:@"catch" withArguments:@[^(JSValue *reason) { reasonWasObject = [reason isObject]; }]];
+
+ checkResult(@"Running code that throws an exception in the executor causes the promise to be rejected", reasonWasObject);
+ }
+}
+
+static void promiseCreateResolved()
+{
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ JSValue *promise = [JSValue valueWithNewPromiseResolvedWithResult:[NSNull null] inContext:context];
+ __block bool calledWithNull = false;
+ [promise invokeMethod:@"then" withArguments:@[
+ ^(JSValue *result) { calledWithNull = [result isNull]; }
+ ]];
+
+ checkResult(@"ResolvedPromise should actually resolve the promise", calledWithNull);
+ }
+}
+
+static void promiseCreateRejected()
+{
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ JSValue *promise = [JSValue valueWithNewPromiseRejectedWithReason:[NSNull null] inContext:context];
+ __block bool calledWithNull = false;
+ [promise invokeMethod:@"then" withArguments:@[
+ [NSNull null],
+ ^(JSValue *result) { calledWithNull = [result isNull]; }
+ ]];
+
+ checkResult(@"RejectedPromise should actually reject the promise", calledWithNull);
+ }
+}
+
+static void parallelPromiseResolveTest()
+{
+ @autoreleasepool {
+ JSContext *context = [[JSContext alloc] init];
+
+ __block RefPtr<Thread> thread;
+
+ Atomic<bool> shouldResolveSoon { false };
+ Atomic<bool> startedThread { false };
+ auto* shouldResolveSoonPtr = &shouldResolveSoon;
+ auto* startedThreadPtr = &startedThread;
+
+ JSValue *promise = [JSValue valueWithNewPromiseInContext:context fromExecutor:^(JSValue *resolve, JSValue *) {
+ thread = Thread::create("async thread", ^() {
+ startedThreadPtr->store(true);
+ while (!shouldResolveSoonPtr->load()) { }
+ [resolve callWithArguments:@[[NSNull null]]];
+ });
+
+ }];
+
+ shouldResolveSoon.store(true);
+ while (!startedThread.load())
+ [context evaluateScript:@"for (let i = 0; i < 10000; i++) { }"];
+
+ thread->waitForCompletion();
+
+ __block bool calledWithNull = false;
+ [promise invokeMethod:@"then" withArguments:@[
+ ^(JSValue *result) { calledWithNull = [result isNull]; }
+ ]];
+
+ checkResult(@"Promise should be resolved", calledWithNull);
+ }
+}
+
void testObjectiveCAPI()
{
NSLog(@"Testing Objective-C API");
checkNegativeNSIntegers();
+ runJITThreadLimitTests();
+
+ promiseWithExecutor(Resolution::ResolveEager);
+ promiseWithExecutor(Resolution::RejectEager);
+ promiseWithExecutor(Resolution::ResolveLate);
+ promiseWithExecutor(Resolution::RejectLate);
+ promiseRejectOnJSException();
+ promiseCreateResolved();
+ promiseCreateRejected();
+ parallelPromiseResolveTest();
+
testObjectiveCAPIMain();
}
Modified: trunk/Source/_javascript_Core/ChangeLog (236371 => 236372)
--- trunk/Source/_javascript_Core/ChangeLog 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/ChangeLog 2018-09-21 23:38:25 UTC (rev 236372)
@@ -1,3 +1,90 @@
+2018-09-21 Keith Miller <[email protected]>
+
+ Add Promise SPI
+ https://bugs.webkit.org/show_bug.cgi?id=189809
+
+ Reviewed by Saam Barati.
+
+ The Patch adds new SPI to create promises. It's mostly SPI because
+ I want to see how internal users react to it before we make it
+ public.
+
+ This patch adds a couple of new Obj-C SPI methods. The first
+ creates a new promise using the same API that JS does where the
+ user provides an executor callback. If an exception is raised
+ in/to that callback the promise is automagically rejected. The
+ other methods create a pre-resolved or rejected promise as this
+ appears to be a common way to initialize a promise.
+
+ I was also considering adding a second version of executor API
+ where it would catch specific Obj-C exceptions. This would work by
+ taking a Class paramter and checking isKindOfClass: on the
+ exception. I decided against this as nothing else in our API
+ handles Obj-C exceptions. I'm pretty sure the VM will end up in a
+ corrupt state if an Obj-C exception unwinds through JS frames.
+
+ This patch adds a new C function that will create a "deferred"
+ promise. A deferred promise is a style of creating promise/futures
+ where the resolve and reject functions are passed as outputs of a
+ function. I went with this style for the C SPI because we don't have
+ any concept of forwarding exceptions in the C API.
+
+ In order to make the C API work I refactored a bit of the promise code
+ so that we can call a static method on JSDeferredPromise and just get
+ the components without allocating an extra cell wrapper.
+
+ * API/JSContext.mm:
+ (+[JSContext currentCallee]):
+ * API/JSObjectRef.cpp:
+ (JSObjectMakeDeferredPromise):
+ * API/JSObjectRefPrivate.h:
+ * API/JSValue.mm:
+ (+[JSValue valueWithNewPromiseInContext:fromExecutor:]):
+ (+[JSValue valueWithNewPromiseResolvedWithResult:inContext:]):
+ (+[JSValue valueWithNewPromiseRejectedWithReason:inContext:]):
+ * API/JSValuePrivate.h: Added.
+ * API/JSVirtualMachine.mm:
+ * API/JSVirtualMachinePrivate.h:
+ * API/tests/testapi.c:
+ (main):
+ * API/tests/testapi.cpp:
+ (APIContext::operator JSC::ExecState*):
+ (TestAPI::failed const):
+ (TestAPI::check):
+ (TestAPI::basicSymbol):
+ (TestAPI::symbolsTypeof):
+ (TestAPI::symbolsGetPropertyForKey):
+ (TestAPI::symbolsSetPropertyForKey):
+ (TestAPI::symbolsHasPropertyForKey):
+ (TestAPI::symbolsDeletePropertyForKey):
+ (TestAPI::promiseResolveTrue):
+ (TestAPI::promiseRejectTrue):
+ (testCAPIViaCpp):
+ (TestAPI::run): Deleted.
+ * API/tests/testapi.mm:
+ (testObjectiveCAPIMain):
+ (promiseWithExecutor):
+ (promiseRejectOnJSException):
+ (promiseCreateResolved):
+ (promiseCreateRejected):
+ (parallelPromiseResolveTest):
+ (testObjectiveCAPI):
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * runtime/JSInternalPromiseDeferred.cpp:
+ (JSC::JSInternalPromiseDeferred::create):
+ * runtime/JSPromise.h:
+ * runtime/JSPromiseConstructor.cpp:
+ (JSC::constructPromise):
+ * runtime/JSPromiseDeferred.cpp:
+ (JSC::JSPromiseDeferred::createDeferredData):
+ (JSC::JSPromiseDeferred::create):
+ (JSC::JSPromiseDeferred::finishCreation):
+ (JSC::newPromiseCapability): Deleted.
+ * runtime/JSPromiseDeferred.h:
+ (JSC::JSPromiseDeferred::promise const):
+ (JSC::JSPromiseDeferred::resolve const):
+ (JSC::JSPromiseDeferred::reject const):
+
2018-09-21 Ryan Haddad <[email protected]>
Unreviewed, rolling out r236359.
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (236371 => 236372)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2018-09-21 23:38:25 UTC (rev 236372)
@@ -1042,6 +1042,7 @@
53C6FEEF1E8ADFA900B18425 /* WasmOpcodeOrigin.h in Headers */ = {isa = PBXBuildFile; fileRef = 53C6FEEE1E8ADFA900B18425 /* WasmOpcodeOrigin.h */; };
53CA730A1EA533D80076049D /* WasmBBQPlan.h in Headers */ = {isa = PBXBuildFile; fileRef = 53CA73081EA533D80076049D /* WasmBBQPlan.h */; };
53D444DC1DAF08AB00B92784 /* B3WasmAddressValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 53D444DB1DAF08AB00B92784 /* B3WasmAddressValue.h */; };
+ 53E1F8F82154715A0001DDBC /* JSValuePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E1F8F7215471490001DDBC /* JSValuePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
53E777E41E92E265007CBEC4 /* WasmModuleInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E777E21E92E265007CBEC4 /* WasmModuleInformation.h */; };
53E9E0AC1EAE83DF00FEE251 /* WasmMachineThreads.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E9E0AA1EAE83DE00FEE251 /* WasmMachineThreads.h */; };
53E9E0AF1EAEC45700FEE251 /* WasmTierUpCount.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E9E0AE1EAEC45700FEE251 /* WasmTierUpCount.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3501,6 +3502,7 @@
53CA73081EA533D80076049D /* WasmBBQPlan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmBBQPlan.h; sourceTree = "<group>"; };
53D444DB1DAF08AB00B92784 /* B3WasmAddressValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3WasmAddressValue.h; path = b3/B3WasmAddressValue.h; sourceTree = "<group>"; };
53D444DD1DAF09A000B92784 /* B3WasmAddressValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3WasmAddressValue.cpp; path = b3/B3WasmAddressValue.cpp; sourceTree = "<group>"; };
+ 53E1F8F7215471490001DDBC /* JSValuePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSValuePrivate.h; sourceTree = "<group>"; };
53E777E11E92E265007CBEC4 /* WasmModuleInformation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmModuleInformation.cpp; sourceTree = "<group>"; };
53E777E21E92E265007CBEC4 /* WasmModuleInformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmModuleInformation.h; sourceTree = "<group>"; };
53E9E0A91EAE83DE00FEE251 /* WasmMachineThreads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmMachineThreads.cpp; sourceTree = "<group>"; };
@@ -5912,6 +5914,7 @@
86E3C606167BAB87006D760A /* JSValue.h */,
86E3C60D167BAB87006D760A /* JSValue.mm */,
86E3C60E167BAB87006D760A /* JSValueInternal.h */,
+ 53E1F8F7215471490001DDBC /* JSValuePrivate.h */,
14BD5A2B0A3E91F600BAF59C /* JSValueRef.cpp */,
1482B6EA0A4300B300517CFC /* JSValueRef.h */,
86E3C60F167BAB87006D760A /* JSVirtualMachine.h */,
@@ -7830,7 +7833,6 @@
isa = PBXGroup;
children = (
99DA00991BD5992700F4575C /* __init__.py */,
- 99DA009D1BD5992700F4575C /* wkbuiltins.py */,
99DA009E1BD5992700F4575C /* builtins_generate_combined_header.py */,
99DA009F1BD5992700F4575C /* builtins_generate_combined_implementation.py */,
412952731D2CF6AC00E78B89 /* builtins_generate_internals_wrapper_header.py */,
@@ -7842,6 +7844,7 @@
99DA009A1BD5992700F4575C /* builtins_generator.py */,
99DA009B1BD5992700F4575C /* builtins_model.py */,
99DA009C1BD5992700F4575C /* builtins_templates.py */,
+ 99DA009D1BD5992700F4575C /* wkbuiltins.py */,
);
path = wkbuiltins;
sourceTree = "<group>";
@@ -8439,7 +8442,6 @@
DE26E9031CB5DD0500D2BE82 /* BuiltinExecutableCreator.h in Headers */,
A7D801A51880D66E0026C39B /* BuiltinExecutables.h in Headers */,
A75EE9B218AAB7E200AAD043 /* BuiltinNames.h in Headers */,
- 99DA00A61BD5993100F4575C /* wkbuiltins.py in Headers */,
99DA00A71BD5993100F4575C /* builtins_generate_combined_header.py in Headers */,
99DA00A81BD5993100F4575C /* builtins_generate_combined_implementation.py in Headers */,
412952771D2CF6BC00E78B89 /* builtins_generate_internals_wrapper_header.py in Headers */,
@@ -9210,6 +9212,7 @@
0F2B670117B6B5AB00A7AE3F /* JSUint8ClampedArray.h in Headers */,
86E3C612167BABD7006D760A /* JSValue.h in Headers */,
86E3C61B167BABEE006D760A /* JSValueInternal.h in Headers */,
+ 53E1F8F82154715A0001DDBC /* JSValuePrivate.h in Headers */,
BC18C42C0E16F5CD00B34460 /* JSValueRef.h in Headers */,
86E3C615167BABD7006D760A /* JSVirtualMachine.h in Headers */,
86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
@@ -9689,6 +9692,7 @@
ADBC54D51DF8EA2B005BF738 /* WebAssemblyToJSCallee.h in Headers */,
52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */,
BC18C47A0E16F5CD00B34460 /* WebKitAvailability.h in Headers */,
+ 99DA00A61BD5993100F4575C /* wkbuiltins.py in Headers */,
A7DCB97312E5193F00911940 /* WriteBarrier.h in Headers */,
C2B6D75318A33793004A9301 /* WriteBarrierInlines.h in Headers */,
0FC8150A14043BF500CFA603 /* WriteBarrierSupport.h in Headers */,
Modified: trunk/Source/_javascript_Core/runtime/JSInternalPromiseDeferred.cpp (236371 => 236372)
--- trunk/Source/_javascript_Core/runtime/JSInternalPromiseDeferred.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/runtime/JSInternalPromiseDeferred.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -42,19 +42,11 @@
VM& vm = exec->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
- JSValue deferred = newPromiseCapability(exec, globalObject, globalObject->internalPromiseConstructor());
+ DeferredData data = "" globalObject, globalObject->internalPromiseConstructor());
RETURN_IF_EXCEPTION(scope, nullptr);
- JSValue promise = deferred.get(exec, vm.propertyNames->builtinNames().promisePrivateName());
- RETURN_IF_EXCEPTION(scope, nullptr);
- ASSERT(promise.inherits<JSInternalPromise>(vm));
- JSValue resolve = deferred.get(exec, vm.propertyNames->builtinNames().resolvePrivateName());
- RETURN_IF_EXCEPTION(scope, nullptr);
- JSValue reject = deferred.get(exec, vm.propertyNames->builtinNames().rejectPrivateName());
- RETURN_IF_EXCEPTION(scope, nullptr);
-
JSInternalPromiseDeferred* result = new (NotNull, allocateCell<JSInternalPromiseDeferred>(vm.heap)) JSInternalPromiseDeferred(vm);
- result->finishCreation(vm, jsCast<JSObject*>(promise), resolve, reject);
+ result->finishCreation(vm, data.promise, data.resolve, data.reject);
return result;
}
Modified: trunk/Source/_javascript_Core/runtime/JSPromise.h (236371 => 236372)
--- trunk/Source/_javascript_Core/runtime/JSPromise.h 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/runtime/JSPromise.h 2018-09-21 23:38:25 UTC (rev 236372)
@@ -34,6 +34,13 @@
typedef JSNonFinalObject Base;
static JSPromise* create(VM&, Structure*);
+ struct JSPromiseAndCallbacks {
+ JSPromise* promise;
+ JSFunction* resolve;
+ JSFunction* reject;
+ };
+ static JSPromiseAndCallbacks createWithCallbacks(VM&, Structure*);
+
static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
DECLARE_EXPORT_INFO;
Modified: trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.cpp (236371 => 236372)
--- trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/runtime/JSPromiseConstructor.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -114,7 +114,7 @@
Structure* promiseStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), globalObject->promiseStructure());
RETURN_IF_EXCEPTION(scope, encodedJSValue());
JSPromise* promise = JSPromise::create(vm, promiseStructure);
- promise->initialize(exec, globalObject, exec->argument(0));
+ promise->initialize(exec, globalObject, exec->argument(0));
RETURN_IF_EXCEPTION(scope, encodedJSValue());
return JSValue::encode(promise);
Modified: trunk/Source/_javascript_Core/runtime/JSPromiseDeferred.cpp (236371 => 236372)
--- trunk/Source/_javascript_Core/runtime/JSPromiseDeferred.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/runtime/JSPromiseDeferred.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -39,8 +39,11 @@
const ClassInfo JSPromiseDeferred::s_info = { "JSPromiseDeferred", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSPromiseDeferred) };
-JSValue newPromiseCapability(ExecState* exec, JSGlobalObject* globalObject, JSPromiseConstructor* promiseConstructor)
+JSPromiseDeferred::DeferredData JSPromiseDeferred::createDeferredData(ExecState* exec, JSGlobalObject* globalObject, JSPromiseConstructor* promiseConstructor)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
JSFunction* newPromiseCapabilityFunction = globalObject->newPromiseCapabilityFunction();
CallData callData;
CallType callType = JSC::getCallData(exec->vm(), newPromiseCapabilityFunction, callData);
@@ -49,30 +52,31 @@
MarkedArgumentBuffer arguments;
arguments.append(promiseConstructor);
ASSERT(!arguments.hasOverflowed());
- return call(exec, newPromiseCapabilityFunction, callType, callData, jsUndefined(), arguments);
+ JSValue deferred = call(exec, newPromiseCapabilityFunction, callType, callData, jsUndefined(), arguments);
+ RETURN_IF_EXCEPTION(scope, { });
+
+ DeferredData result;
+ result.promise = jsCast<JSPromise*>(deferred.get(exec, vm.propertyNames->builtinNames().promisePrivateName()));
+ RETURN_IF_EXCEPTION(scope, { });
+ result.resolve = jsCast<JSFunction*>(deferred.get(exec, vm.propertyNames->builtinNames().resolvePrivateName()));
+ RETURN_IF_EXCEPTION(scope, { });
+ result.reject = jsCast<JSFunction*>(deferred.get(exec, vm.propertyNames->builtinNames().rejectPrivateName()));
+ RETURN_IF_EXCEPTION(scope, { });
+
+ return result;
}
-
JSPromiseDeferred* JSPromiseDeferred::create(ExecState* exec, JSGlobalObject* globalObject)
{
VM& vm = exec->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
- JSValue deferred = newPromiseCapability(exec, globalObject, globalObject->promiseConstructor());
- RETURN_IF_EXCEPTION(scope, nullptr);
-
- JSValue promise = deferred.get(exec, vm.propertyNames->builtinNames().promisePrivateName());
- RETURN_IF_EXCEPTION(scope, nullptr);
- ASSERT(promise.inherits<JSPromise>(vm));
- JSValue resolve = deferred.get(exec, vm.propertyNames->builtinNames().resolvePrivateName());
- RETURN_IF_EXCEPTION(scope, nullptr);
- JSValue reject = deferred.get(exec, vm.propertyNames->builtinNames().rejectPrivateName());
- RETURN_IF_EXCEPTION(scope, nullptr);
-
- return JSPromiseDeferred::create(vm, jsCast<JSPromise*>(promise), resolve, reject);
+ DeferredData data = "" globalObject, globalObject->promiseConstructor());
+ RETURN_IF_EXCEPTION(scope, { });
+ return JSPromiseDeferred::create(vm, data.promise, data.resolve, data.reject);
}
-JSPromiseDeferred* JSPromiseDeferred::create(VM& vm, JSObject* promise, JSValue resolve, JSValue reject)
+JSPromiseDeferred* JSPromiseDeferred::create(VM& vm, JSPromise* promise, JSFunction* resolve, JSFunction* reject)
{
JSPromiseDeferred* deferred = new (NotNull, allocateCell<JSPromiseDeferred>(vm.heap)) JSPromiseDeferred(vm);
deferred->finishCreation(vm, promise, resolve, reject);
@@ -121,7 +125,7 @@
reject(exec, reason->value());
}
-void JSPromiseDeferred::finishCreation(VM& vm, JSObject* promise, JSValue resolve, JSValue reject)
+void JSPromiseDeferred::finishCreation(VM& vm, JSPromise* promise, JSFunction* resolve, JSFunction* reject)
{
Base::finishCreation(vm);
m_promise.set(vm, this, promise);
Modified: trunk/Source/_javascript_Core/runtime/JSPromiseDeferred.h (236371 => 236372)
--- trunk/Source/_javascript_Core/runtime/JSPromiseDeferred.h 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/_javascript_Core/runtime/JSPromiseDeferred.h 2018-09-21 23:38:25 UTC (rev 236372)
@@ -26,6 +26,7 @@
#pragma once
#include "JSCast.h"
+#include "JSPromise.h"
#include "Structure.h"
namespace JSC {
@@ -32,6 +33,7 @@
class Exception;
class JSPromiseConstructor;
+class JSFunction;
class JSPromiseDeferred : public JSCell {
public:
@@ -38,8 +40,17 @@
typedef JSCell Base;
static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+ struct DeferredData {
+ WTF_FORBID_HEAP_ALLOCATION;
+ public:
+ JSPromise* promise { nullptr };
+ JSFunction* resolve { nullptr };
+ JSFunction* reject { nullptr };
+ };
+ static DeferredData createDeferredData(ExecState*, JSGlobalObject*, JSPromiseConstructor*);
+
JS_EXPORT_PRIVATE static JSPromiseDeferred* create(ExecState*, JSGlobalObject*);
- JS_EXPORT_PRIVATE static JSPromiseDeferred* create(VM&, JSObject* promise, JSValue resolve, JSValue reject);
+ JS_EXPORT_PRIVATE static JSPromiseDeferred* create(VM&, JSPromise*, JSFunction* resolve, JSFunction* reject);
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
{
@@ -48,9 +59,9 @@
DECLARE_EXPORT_INFO;
- JSObject* promise() const { return m_promise.get(); }
- JSValue resolve() const { return m_resolve.get(); }
- JSValue reject() const { return m_reject.get(); }
+ JSPromise* promise() const { return m_promise.get(); }
+ JSFunction* resolve() const { return m_resolve.get(); }
+ JSFunction* reject() const { return m_reject.get(); }
JS_EXPORT_PRIVATE void resolve(ExecState*, JSValue);
JS_EXPORT_PRIVATE void reject(ExecState*, JSValue);
@@ -62,7 +73,7 @@
protected:
JSPromiseDeferred(VM&, Structure*);
- void finishCreation(VM&, JSObject*, JSValue, JSValue);
+ void finishCreation(VM&, JSPromise*, JSFunction* resolve, JSFunction* reject);
static void visitChildren(JSCell*, SlotVisitor&);
private:
@@ -72,11 +83,9 @@
bool m_promiseIsAsyncPending { false };
#endif
- WriteBarrier<JSObject> m_promise;
- WriteBarrier<Unknown> m_resolve;
- WriteBarrier<Unknown> m_reject;
+ WriteBarrier<JSPromise> m_promise;
+ WriteBarrier<JSFunction> m_resolve;
+ WriteBarrier<JSFunction> m_reject;
};
-JSValue newPromiseCapability(ExecState*, JSGlobalObject*, JSPromiseConstructor*);
-
} // namespace JSC
Modified: trunk/Source/WTF/ChangeLog (236371 => 236372)
--- trunk/Source/WTF/ChangeLog 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/WTF/ChangeLog 2018-09-21 23:38:25 UTC (rev 236372)
@@ -1,3 +1,22 @@
+2018-09-21 Keith Miller <[email protected]>
+
+ Add Promise SPI
+ https://bugs.webkit.org/show_bug.cgi?id=189809
+
+ Reviewed by Saam Barati.
+
+ Fix issue where creating a JSContextRef off the main thread before
+ creating initializing the main thread would cause an assertion
+ failure.
+
+ * wtf/MainThread.cpp:
+ (WTF::isMainThreadIfInitialized):
+ * wtf/MainThread.h:
+ * wtf/mac/MainThreadMac.mm:
+ (WTF::isMainThreadIfInitialized):
+ * wtf/text/cf/StringImplCF.cpp:
+ (WTF::StringImpl::createCFString):
+
2018-09-21 Ryan Haddad <[email protected]>
Unreviewed, rolling out r236359.
Modified: trunk/Source/WTF/wtf/MainThread.cpp (236371 => 236372)
--- trunk/Source/WTF/wtf/MainThread.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/WTF/wtf/MainThread.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -73,6 +73,11 @@
{
return mainThread == &Thread::current();
}
+
+bool isMainThreadIfInitialized()
+{
+ return isMainThread();
+}
#endif
#if PLATFORM(COCOA)
Modified: trunk/Source/WTF/wtf/MainThread.h (236371 => 236372)
--- trunk/Source/WTF/wtf/MainThread.h 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/WTF/wtf/MainThread.h 2018-09-21 23:38:25 UTC (rev 236372)
@@ -54,6 +54,7 @@
WTF_EXPORT_PRIVATE void setMainThreadCallbacksPaused(bool paused);
WTF_EXPORT_PRIVATE bool isMainThread();
+WTF_EXPORT_PRIVATE bool isMainThreadIfInitialized();
WTF_EXPORT_PRIVATE bool canAccessThreadLocalDataForThread(Thread&);
Modified: trunk/Source/WTF/wtf/mac/MainThreadMac.mm (236371 => 236372)
--- trunk/Source/WTF/wtf/mac/MainThreadMac.mm 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/WTF/wtf/mac/MainThreadMac.mm 2018-09-21 23:38:25 UTC (rev 236372)
@@ -60,9 +60,9 @@
static JSWTFMainThreadCaller* staticMainThreadCaller;
static bool isTimerPosted; // This is only accessed on the main thread.
-static bool mainThreadEstablishedAsPthreadMain;
-static pthread_t mainThreadPthread;
-static NSThread* mainThreadNSThread;
+static bool mainThreadEstablishedAsPthreadMain { false };
+static pthread_t mainThreadPthread { nullptr };
+static NSThread* mainThreadNSThread { nullptr };
#if USE(WEB_THREAD)
static Thread* sApplicationUIThread;
@@ -162,6 +162,11 @@
return (isWebThread() || pthread_main_np()) && webThreadIsUninitializedOrLockedOrDisabled();
}
+bool isMainThreadIfInitialized()
+{
+ return isMainThread();
+}
+
bool isUIThread()
{
return pthread_main_np();
@@ -212,6 +217,14 @@
ASSERT(mainThreadPthread);
return pthread_equal(pthread_self(), mainThreadPthread);
}
+
+bool isMainThreadIfInitialized()
+{
+ if (mainThreadEstablishedAsPthreadMain)
+ return pthread_main_np();
+ return pthread_equal(pthread_self(), mainThreadPthread);
+}
+
#endif // USE(WEB_THREAD)
} // namespace WTF
Modified: trunk/Source/WTF/wtf/text/cf/StringImplCF.cpp (236371 => 236372)
--- trunk/Source/WTF/wtf/text/cf/StringImplCF.cpp 2018-09-21 23:22:05 UTC (rev 236371)
+++ trunk/Source/WTF/wtf/text/cf/StringImplCF.cpp 2018-09-21 23:38:25 UTC (rev 236372)
@@ -122,7 +122,7 @@
RetainPtr<CFStringRef> StringImpl::createCFString()
{
- if (!m_length || !isMainThread()) {
+ if (!m_length || !isMainThreadIfInitialized()) {
if (is8Bit())
return adoptCF(CFStringCreateWithBytes(0, reinterpret_cast<const UInt8*>(characters8()), m_length, kCFStringEncodingISOLatin1, false));
return adoptCF(CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar*>(characters16()), m_length));