JSC should have a module loader API
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Jan 2019 20:27:17 +0000 (20:27 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Jan 2019 20:27:17 +0000 (20:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191121

Reviewed by Michael Saboff.

This patch adds a new delegate to JSContext that is called to fetch
any resolved module. The resolution of a module identifier is computed
as if it were a URL on the web with the caveat that it must be a file URL.

A new class JSScript has also been added that is similar to JSScriptRef.
Right now all JSScripts are copied into memory. In the future we should
mmap the provided file into memory so the OS can evict it to disk under
pressure. Additionally, the API does not make use of the code signing path
nor the bytecode caching path, which we will add in subsequent patches.

Lastly, a couple of new convenience methods have been added. C API
conversion, can now toRef a JSValue with just a vm rather than
requiring an ExecState. Secondly, there is now a call wrapper that
does not require CallData and CallType since many places don't
care about this.

* API/APICast.h:
(toRef):
* API/JSAPIGlobalObject.cpp: Copied from Source/JavaScriptCore/API/JSVirtualMachineInternal.h.
* API/JSAPIGlobalObject.h: Added.
(JSC::JSAPIGlobalObject::create):
(JSC::JSAPIGlobalObject::createStructure):
(JSC::JSAPIGlobalObject::JSAPIGlobalObject):
* API/JSAPIGlobalObject.mm: Added.
(JSC::JSAPIGlobalObject::moduleLoaderResolve):
(JSC::JSAPIGlobalObject::moduleLoaderImportModule):
(JSC::JSAPIGlobalObject::moduleLoaderFetch):
(JSC::JSAPIGlobalObject::moduleLoaderCreateImportMetaProperties):
* API/JSAPIValueWrapper.h:
(JSC::jsAPIValueWrapper): Deleted.
* API/JSContext.h:
* API/JSContext.mm:
(-[JSContext moduleLoaderDelegate]):
(-[JSContext setModuleLoaderDelegate:]):
* API/JSContextInternal.h:
* API/JSContextPrivate.h:
* API/JSContextRef.cpp:
(JSGlobalContextCreateInGroup):
* API/JSScript.h: Added.
* API/JSScript.mm: Added.
(+[JSScript scriptWithSource:inVirtualMachine:]):
(fillBufferWithContentsOfFile):
(+[JSScript scriptFromUTF8File:inVirtualMachine:withCodeSigning:andBytecodeCache:]):
(getJSScriptSourceCode):
* API/JSScriptInternal.h: Copied from Source/JavaScriptCore/API/JSVirtualMachineInternal.h.
* API/JSValueInternal.h:
* API/JSVirtualMachineInternal.h:
* API/tests/testapi.mm:
(+[JSContextFetchDelegate contextWithBlockForFetch:]):
(-[JSContextFetchDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
(checkModuleCodeRan):
(checkModuleWasRejected):
(testFetch):
(testFetchWithTwoCycle):
(testFetchWithThreeCycle):
(testLoaderResolvesAbsoluteScriptURL):
(testLoaderRejectsNilScriptURL):
(testLoaderRejectsFailedFetch):
(testImportModuleTwice):
(+[JSContextFileLoaderDelegate newContext]):
(resolvePathToScripts):
(-[JSContextFileLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
(testLoadBasicFile):
(testObjectiveCAPI):
* API/tests/testapiScripts/basic.js: Copied from Source/JavaScriptCore/API/JSVirtualMachineInternal.h.
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* SourcesCocoa.txt:
* config.h:
* postprocess-headers.sh:
* runtime/CallData.cpp:
(JSC::call):
* runtime/CallData.h:
* runtime/Completion.cpp:
(JSC::loadAndEvaluateModule):
* runtime/Completion.h:
* runtime/JSCast.h:
(JSC::jsSecureCast):
* runtime/JSGlobalObject.cpp:
(JSC::createProxyProperty):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239933 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
Source/JavaScriptCore/API/APICast.h
Source/JavaScriptCore/API/JSAPIGlobalObject.cpp [new file with mode: 0644]
Source/JavaScriptCore/API/JSAPIGlobalObject.h [new file with mode: 0644]
Source/JavaScriptCore/API/JSAPIGlobalObject.mm [new file with mode: 0644]
Source/JavaScriptCore/API/JSAPIValueWrapper.h
Source/JavaScriptCore/API/JSContext.h
Source/JavaScriptCore/API/JSContext.mm
Source/JavaScriptCore/API/JSContextInternal.h
Source/JavaScriptCore/API/JSContextPrivate.h
Source/JavaScriptCore/API/JSContextRef.cpp
Source/JavaScriptCore/API/JSScript.h [new file with mode: 0644]
Source/JavaScriptCore/API/JSScript.mm [new file with mode: 0644]
Source/JavaScriptCore/API/JSScriptInternal.h [new file with mode: 0644]
Source/JavaScriptCore/API/JSValueInternal.h
Source/JavaScriptCore/API/JSVirtualMachineInternal.h
Source/JavaScriptCore/API/tests/testapi.mm
Source/JavaScriptCore/API/tests/testapiScripts/basic.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/SourcesCocoa.txt
Source/JavaScriptCore/config.h
Source/JavaScriptCore/postprocess-headers.sh
Source/JavaScriptCore/runtime/CallData.cpp
Source/JavaScriptCore/runtime/CallData.h
Source/JavaScriptCore/runtime/Completion.cpp
Source/JavaScriptCore/runtime/Completion.h
Source/JavaScriptCore/runtime/JSCast.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp

index 3b55911..ae9bc07 100644 (file)
@@ -128,21 +128,26 @@ inline JSC::VM* toJS(JSContextGroupRef g)
     return reinterpret_cast<JSC::VM*>(const_cast<OpaqueJSContextGroup*>(g));
 }
 
-inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v)
+inline JSValueRef toRef(JSC::VM& vm, JSC::JSValue v)
 {
-    ASSERT(exec->vm().currentThreadIsHoldingAPILock());
+    ASSERT(vm.currentThreadIsHoldingAPILock());
 #if !CPU(ADDRESS64)
     if (!v)
         return 0;
     if (!v.isCell())
-        return reinterpret_cast<JSValueRef>(JSC::jsAPIValueWrapper(exec, v).asCell());
+        return reinterpret_cast<JSValueRef>(JSC::JSAPIValueWrapper::create(vm, v));
     return reinterpret_cast<JSValueRef>(v.asCell());
 #else
-    UNUSED_PARAM(exec);
+    UNUSED_PARAM(vm);
     return bitwise_cast<JSValueRef>(v);
 #endif
 }
 
+inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v)
+{
+    return toRef(exec->vm(), v);
+}
+
 inline JSObjectRef toRef(JSC::JSObject* o)
 {
     return reinterpret_cast<JSObjectRef>(o);
diff --git a/Source/JavaScriptCore/API/JSAPIGlobalObject.cpp b/Source/JavaScriptCore/API/JSAPIGlobalObject.cpp
new file mode 100644 (file)
index 0000000..d75c58d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 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 "JSAPIGlobalObject.h"
+
+#if !JSC_OBJC_API_ENABLED
+
+namespace JSC {
+
+const ClassInfo JSAPIGlobalObject::s_info = { "GlobalObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSAPIGlobalObject) };
+
+const GlobalObjectMethodTable JSAPIGlobalObject::s_globalObjectMethodTable = {
+    &supportsRichSourceInfo,
+    &shouldInterruptScript,
+    &javaScriptRuntimeFlags,
+    nullptr, // queueTaskToEventLoop
+    &shouldInterruptScriptBeforeTimeout,
+    nullptr, // moduleLoaderImportModule
+    nullptr, // moduleLoaderResolve
+    nullptr, // moduleLoaderFetch
+    nullptr, // moduleLoaderCreateImportMetaProperties
+    nullptr, // moduleLoaderEvaluate
+    nullptr, // promiseRejectionTracker
+    nullptr, // defaultLanguage
+    nullptr, // compileStreaming
+    nullptr, // instantiateStreaming
+};
+
+}
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSAPIGlobalObject.h b/Source/JavaScriptCore/API/JSAPIGlobalObject.h
new file mode 100644 (file)
index 0000000..5d5f12c
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "JSGlobalObject.h"
+
+namespace JSC {
+
+class JSAPIGlobalObject : public JSGlobalObject {
+public:
+    using Base = JSGlobalObject;
+
+    DECLARE_EXPORT_INFO;
+    static const GlobalObjectMethodTable s_globalObjectMethodTable;
+
+    static JSAPIGlobalObject* create(VM& vm, Structure* structure)
+    {
+        auto* object = new (NotNull, allocateCell<JSAPIGlobalObject>(vm.heap)) JSAPIGlobalObject(vm, structure);
+        object->finishCreation(vm);
+        return object;
+    }
+
+    static Structure* createStructure(VM& vm, JSValue prototype)
+    {
+        auto* result = Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
+        result->setTransitionWatchpointIsLikelyToBeFired(true);
+        return result;
+    }
+
+    static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString* moduleNameValue, JSValue parameters, const SourceOrigin&);
+    static Identifier moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue);
+    static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
+    static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue);
+
+private:
+    JSAPIGlobalObject(VM& vm, Structure* structure)
+        : Base(vm, structure, &s_globalObjectMethodTable)
+    { }
+};
+
+}
diff --git a/Source/JavaScriptCore/API/JSAPIGlobalObject.mm b/Source/JavaScriptCore/API/JSAPIGlobalObject.mm
new file mode 100644 (file)
index 0000000..3ca4ab9
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 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 "config.h"
+#import "JSAPIGlobalObject.h"
+
+#if JSC_OBJC_API_ENABLED
+
+#import "APICast.h"
+#import "CatchScope.h"
+#import "Completion.h"
+#import "Error.h"
+#import "Exception.h"
+#import "JSContextInternal.h"
+#import "JSInternalPromiseDeferred.h"
+#import "JSNativeStdFunction.h"
+#import "JSScriptInternal.h"
+#import "JSSourceCode.h"
+#import "JSValueInternal.h"
+#import "JSVirtualMachineInternal.h"
+#import "JavaScriptCore.h"
+#import "ObjectConstructor.h"
+#import "SourceOrigin.h"
+
+#import <wtf/URL.h>
+
+namespace JSC {
+
+const ClassInfo JSAPIGlobalObject::s_info = { "GlobalObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSAPIGlobalObject) };
+
+const GlobalObjectMethodTable JSAPIGlobalObject::s_globalObjectMethodTable = {
+    &supportsRichSourceInfo,
+    &shouldInterruptScript,
+    &javaScriptRuntimeFlags,
+    nullptr, // queueTaskToEventLoop
+    &shouldInterruptScriptBeforeTimeout,
+    &moduleLoaderImportModule, // moduleLoaderImportModule
+    &moduleLoaderResolve, // moduleLoaderResolve
+    &moduleLoaderFetch, // moduleLoaderFetch
+    &moduleLoaderCreateImportMetaProperties, // moduleLoaderCreateImportMetaProperties
+    nullptr, // moduleLoaderEvaluate
+    nullptr, // promiseRejectionTracker
+    nullptr, // defaultLanguage
+    nullptr, // compileStreaming
+    nullptr, // instantiateStreaming
+};
+
+Identifier JSAPIGlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue referrer, JSValue)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    ASSERT_UNUSED(globalObject, globalObject == exec->lexicalGlobalObject());
+    ASSERT(key.isString() || key.isSymbol());
+    String name =  key.toWTFString(exec);
+
+    URL referrerURL(URL(), jsCast<JSString*>(referrer)->tryGetValue());
+    RELEASE_ASSERT(referrerURL.isValid());
+
+    URL url = URL(referrerURL, name);
+    if (url.isValid())
+        return Identifier::fromString(exec, url);
+
+    throwVMError(exec, scope, "Could not form valid URL from identifier and base"_s);
+    return { };
+}
+
+JSInternalPromise* JSAPIGlobalObject::moduleLoaderImportModule(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSString* specifierValue, JSValue, const SourceOrigin& sourceOrigin)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    auto reject = [&] (JSValue exception) -> JSInternalPromise* {
+        scope.clearException();
+        auto* promise = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
+        scope.clearException();
+        return promise->reject(exec, exception);
+    };
+
+    auto import = [&] (URL& url) {
+        auto result = importModule(exec, Identifier::fromString(&vm, url), jsUndefined(), jsUndefined());
+        if (UNLIKELY(scope.exception()))
+            return reject(scope.exception());
+        return result;
+    };
+
+    auto specifier = specifierValue->value(exec);
+    if (UNLIKELY(scope.exception())) {
+        JSValue exception = scope.exception();
+        scope.clearException();
+        return reject(exception);
+    }
+
+    URL absoluteURL(URL(), specifier);
+    if (absoluteURL.isValid())
+        return import(absoluteURL);
+
+    if (!specifier.startsWith('/') && !specifier.startsWith("./") && !specifier.startsWith("../"))
+        return reject(createError(exec, "Module specifier does not start with \"/\", \"./\", or \"../\"."_s));
+
+    if (specifier.startsWith('/')) {
+        absoluteURL = URL(URL({ }, "file://"), specifier);
+        if (absoluteURL.isValid())
+            return import(absoluteURL);
+    }
+
+    auto noBaseErrorMessage = "Could not determine the base URL for loading."_s;
+    if (sourceOrigin.isNull())
+        return reject(createError(exec, noBaseErrorMessage));
+
+    auto referrer = sourceOrigin.string();
+    URL baseURL(URL(), referrer);
+    if (!baseURL.isValid())
+        return reject(createError(exec, noBaseErrorMessage));
+
+    URL url(baseURL, specifier);
+    if (!url.isValid())
+        return reject(createError(exec, "could not determine a valid URL for module specifier"_s));
+
+    return import(url);
+}
+
+JSInternalPromise* JSAPIGlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue, JSValue)
+{
+    VM& vm = globalObject->vm();
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+
+    ASSERT(globalObject == exec->lexicalGlobalObject());
+    JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject->globalExec())];
+
+    JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
+    Identifier moduleKey = key.toPropertyKey(exec);
+    if (UNLIKELY(scope.exception())) {
+        JSValue exception = scope.exception();
+        scope.clearException();
+        return deferred->reject(exec, exception);
+    }
+
+    if (UNLIKELY(![context moduleLoaderDelegate]))
+        return deferred->reject(exec, createError(exec, "No module loader provided."));
+
+    auto deferredPromise = Strong<JSInternalPromiseDeferred>(vm, deferred);
+    auto strongKey = Strong<JSString>(vm, jsSecureCast<JSString*>(vm, key));
+    auto* resolve = JSNativeStdFunction::create(vm, globalObject, 1, "resolve", [=] (ExecState* exec) {
+        VM& vm = exec->vm();
+        // This captures the globalObject but that's ok because our structure keeps it alive anyway.
+        JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject->globalExec())];
+        id script = valueToObject(context, toRef(exec, exec->argument(0)));
+
+        MarkedArgumentBuffer args;
+        if (UNLIKELY(![script isKindOfClass:[JSScript class]])) {
+            args.append(createTypeError(exec, "First argument of resolution callback is not a JSScript"));
+            call(exec, deferredPromise->JSPromiseDeferred::reject(), args, "This should never be seen...");
+            return encodedJSUndefined();
+        }
+
+        const String& source = getJSScriptSourceCode(static_cast<JSScript *>(script));
+        args.append(JSSourceCode::create(vm, makeSource(source, SourceOrigin(moduleKey.string()), URL({ }, moduleKey.string()), TextPosition(), JSC::SourceProviderSourceType::Module)));
+        call(exec, deferredPromise->JSPromiseDeferred::resolve(), args, "This should never be seen...");
+        return encodedJSUndefined();
+    });
+
+    auto* reject = JSNativeStdFunction::create(vm, globalObject, 1, "reject", [=] (ExecState* exec) {
+        MarkedArgumentBuffer args;
+        args.append(exec->argument(0));
+
+        call(exec, deferredPromise->JSPromiseDeferred::reject(), args, "This should never be seen...");
+        return encodedJSUndefined();
+    });
+
+    [[context moduleLoaderDelegate] context:context fetchModuleForIdentifier:[::JSValue valueWithJSValueRef:toRef(exec, key) inContext:context] withResolveHandler:[::JSValue valueWithJSValueRef:toRef(exec, resolve) inContext:context] andRejectHandler:[::JSValue valueWithJSValueRef:toRef(exec, reject) inContext:context]];
+    if (context.exception) {
+        deferred->reject(exec, toJS(exec, [context.exception JSValueRef]));
+        context.exception = nil;
+    }
+    return deferred->promise();
+}
+
+JSObject* JSAPIGlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSModuleRecord*, JSValue)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* metaProperties = constructEmptyObject(exec, globalObject->nullPrototypeObjectStructure());
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
+    metaProperties->putDirect(vm, Identifier::fromString(&vm, "filename"), key);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
+    return metaProperties;
+}
+
+}
+
+#endif // JSC_OBJC_API_ENABLED
index 06b3071..aa26082 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003, 2004, 2005, 2007, 2008 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003-2019 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -44,10 +44,9 @@ public:
 
     DECLARE_EXPORT_INFO;
 
-    static JSAPIValueWrapper* create(ExecState* exec, JSValue value) 
+    static JSAPIValueWrapper* create(VM& vm, JSValue value)
     {
-        VM& vm = exec->vm();
-        JSAPIValueWrapper* wrapper = new (NotNull, allocateCell<JSAPIValueWrapper>(vm.heap)) JSAPIValueWrapper(exec);
+        JSAPIValueWrapper* wrapper = new (NotNull, allocateCell<JSAPIValueWrapper>(vm.heap)) JSAPIValueWrapper(vm);
         wrapper->finishCreation(vm, value);
         return wrapper;
     }
@@ -61,17 +60,12 @@ protected:
     }
 
 private:
-    JSAPIValueWrapper(ExecState* exec)
-        : JSCell(exec->vm(), exec->vm().apiWrapperStructure.get())
+    JSAPIValueWrapper(VM& vm)
+        : JSCell(vm, vm.apiWrapperStructure.get())
     {
     }
 
     WriteBarrier<Unknown> m_value;
 };
 
-inline JSValue jsAPIValueWrapper(ExecState* exec, JSValue value)
-{
-    return JSAPIValueWrapper::create(exec, value);
-}
-
 } // namespace JSC
index 89c87f9..1790e59 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,7 +31,7 @@
 
 #if JSC_OBJC_API_ENABLED
 
-@class JSVirtualMachine, JSValue;
+@class JSScript, JSVirtualMachine, JSValue, JSContext;
 
 /*!
 @interface
@@ -39,7 +39,7 @@
  JavaScript execution takes place within a context, and all JavaScript values
  are tied to a context.
 */
-NS_CLASS_AVAILABLE(10_9, 7_0)
+JSC_CLASS_AVAILABLE(macosx(10.9), ios(7.0))
 @interface JSContext : NSObject
 
 /*!
@@ -177,7 +177,6 @@ NS_CLASS_AVAILABLE(10_9, 7_0)
 @discussion Name of the JSContext. Exposed when remote debugging the context.
 */
 @property (copy) NSString *name JSC_API_AVAILABLE(macosx(10.10), ios(8.0));
-
 @end
 
 /*!
@@ -231,6 +230,7 @@ NS_CLASS_AVAILABLE(10_9, 7_0)
 @result The C API equivalent of this JSContext.
 */
 @property (readonly) JSGlobalContextRef JSGlobalContextRef;
+
 @end
 
 #endif
index 27566f4..3c776d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "config.h"
 
 #import "APICast.h"
+#import "Completion.h"
 #import "JSCInlines.h"
 #import "JSContextInternal.h"
 #import "JSContextPrivate.h"
 #import "JSContextRefInternal.h"
 #import "JSGlobalObject.h"
+#import "JSInternalPromise.h"
+#import "JSModuleLoader.h"
+#import "JSScriptInternal.h"
 #import "JSValueInternal.h"
 #import "JSVirtualMachineInternal.h"
 #import "JSWrapperMap.h"
 #import "ObjcRuntimeExtras.h"
 #import "StrongInlines.h"
 
+#import <wtf/WeakObjCPtr.h>
+
 #if JSC_OBJC_API_ENABLED
 
 @implementation JSContext {
     JSVirtualMachine *m_virtualMachine;
     JSGlobalContextRef m_context;
     JSC::Strong<JSC::JSObject> m_exception;
+    WeakObjCPtr<id <JSModuleLoaderDelegate>> m_moduleLoaderDelegate;
 }
 
 - (JSGlobalContextRef)JSGlobalContextRef
     JSGlobalContextSetDebuggerRunLoop(m_context, runLoop);
 }
 
+- (id<JSModuleLoaderDelegate>)moduleLoaderDelegate
+{
+    return m_moduleLoaderDelegate.getAutoreleased();
+}
+
+- (void)setModuleLoaderDelegate:(id<JSModuleLoaderDelegate>)moduleLoaderDelegate
+{
+    m_moduleLoaderDelegate = moduleLoaderDelegate;
+}
+
 @end
 
 @implementation JSContext(SubscriptSupport)
index 28f709c..ab45829 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,7 +27,7 @@
 
 #if JSC_OBJC_API_ENABLED
 
-#import <JavaScriptCore/JSContext.h>
+#import <JavaScriptCore/JSContextPrivate.h>
 
 struct CallbackData {
     CallbackData* next;
index ff9622a..d4e206b 100644 (file)
 
 #import <JavaScriptCore/JSContext.h>
 
+@protocol JSModuleLoaderDelegate <NSObject>
+
+@required
+
+/*! @abstract Provides source code for any JS module that is actively imported.
+ @param context The context for which the module is being requested.
+ @param identifier The resolved identifier for the requested module.
+ @param resolve A JS function to call with the desired script for identifier.
+ @param reject A JS function to call when identifier cannot be fetched.
+ @discussion Currently, identifier will always be an absolute file URL computed from specifier of the requested module relative to the URL of the requesting script. If the requesting script does not have a URL and the module specifier is not an absolute path the module loader will fail to load the module.
+
+ The first argument to resolve sholud always be a JSScript, otherwise the module loader will reject the module.
+
+ Once an identifier has been resolved or rejected in a given context it will never be requested again. If a script is successfully evaluated it will not be re-evaluated on any subsequent import.
+
+ The VM will retain all evaluated modules for the lifetime of the context.
+ */
+- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject;
+
+@end
+
 @interface JSContext(Private)
 
 /*!
@@ -50,6 +71,9 @@
 */
 @property (setter=_setDebuggerRunLoop:) CFRunLoopRef _debuggerRunLoop JSC_API_AVAILABLE(macosx(10.10), ios(8.0));
 
+/*! @abstract The delegate the context will use when trying to load a module. Note, this delegate will be ignored for contexts returned by UIWebView. */
+@property (nonatomic, weak) id <JSModuleLoaderDelegate> moduleLoaderDelegate JSC_API_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA));
+
 @end
 
 #endif
index e18dfd2..9f5fdaf 100644 (file)
@@ -30,9 +30,9 @@
 #include "APICast.h"
 #include "CallFrame.h"
 #include "InitializeThreading.h"
+#include "JSAPIGlobalObject.h"
 #include "JSCallbackObject.h"
 #include "JSClassRef.h"
-#include "JSGlobalObject.h"
 #include "JSObject.h"
 #include "JSCInlines.h"
 #include "SourceProvider.h"
@@ -138,7 +138,7 @@ JSGlobalContextRef JSGlobalContextCreateInGroup(JSContextGroupRef group, JSClass
     JSLockHolder locker(vm.ptr());
 
     if (!globalObjectClass) {
-        JSGlobalObject* globalObject = JSGlobalObject::create(vm.get(), JSGlobalObject::createStructure(vm.get(), jsNull()));
+        JSGlobalObject* globalObject = JSAPIGlobalObject::create(vm.get(), JSAPIGlobalObject::createStructure(vm.get(), jsNull()));
 #if ENABLE(REMOTE_INSPECTOR)
         if (JSRemoteInspectorGetInspectionEnabledByDefault())
             globalObject->setRemoteDebuggingEnabled(true);
diff --git a/Source/JavaScriptCore/API/JSScript.h b/Source/JavaScriptCore/API/JSScript.h
new file mode 100644 (file)
index 0000000..90cb609
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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/JSValue.h>
+
+#if JSC_OBJC_API_ENABLED
+
+NS_ASSUME_NONNULL_BEGIN
+
+OBJC_CLASS JSVirtualMachine;
+
+JSC_CLASS_AVAILABLE(macosx(JSC_MAC_TBA), ios(JSC_IOS_TBA))
+@interface JSScript : NSObject
+
+/*!
+ @method
+ @abstract Create a JSScript for the specified virtual machine.
+ @param source The source code to use when the script is evaluated by the JS vm.
+ @param vm The JSVirtualMachine the script can be evaluated in.
+ @result The new script.
+ */
++ (nullable instancetype)scriptWithSource:(NSString *)source inVirtualMachine:(JSVirtualMachine *)vm;
+
+/*!
+ @method
+ @abstract Create a JSScript for the specified virtual machine with a path to a codesigning and bytecode caching.
+ @param filePath A URL containing the path to a JS source code file on disk.
+ @param vm The JSVirtualMachine the script can be evaluated in.
+ @param codeSigningPath A URL containing the path to the codeSigning file for filePath on disk.
+ @param cachePath A URL containing the path where the VM should cache for future execution.
+ @result The new script.
+ @discussion the files at filePath, codeSigningPath, and cachePath should not be externally modified  for the lifecycle of vm. Note that codeSigningPath and cachePath are not used currently, but that will change in the near future.
+ */
++ (nullable instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(nullable NSURL *)codeSigningPath andBytecodeCache:(nullable NSURL *)cachePath;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // JSC_OBJC_API_ENABLED
diff --git a/Source/JavaScriptCore/API/JSScript.mm b/Source/JavaScriptCore/API/JSScript.mm
new file mode 100644 (file)
index 0000000..796c41e
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 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 "config.h"
+#import "JSScriptInternal.h"
+
+#import "APICast.h"
+#import "JSContextInternal.h"
+#import "JSValuePrivate.h"
+#import "Symbol.h"
+
+#if JSC_OBJC_API_ENABLED
+
+@implementation JSScript {
+    String m_source;
+}
+
++ (instancetype)scriptWithSource:(NSString *)source inVirtualMachine:(JSVirtualMachine *)vm
+{
+    UNUSED_PARAM(vm);
+    JSScript *result = [[JSScript alloc] init];
+    result->m_source = source;
+    return result;
+}
+
+template<typename Vector>
+static bool fillBufferWithContentsOfFile(FILE* file, Vector& buffer)
+{
+    // We might have injected "use strict"; at the top.
+    size_t initialSize = buffer.size();
+    if (fseek(file, 0, SEEK_END) == -1)
+        return false;
+    long bufferCapacity = ftell(file);
+    if (bufferCapacity == -1)
+        return false;
+    if (fseek(file, 0, SEEK_SET) == -1)
+        return false;
+    buffer.resize(bufferCapacity + initialSize);
+    size_t readSize = fread(buffer.data() + initialSize, 1, buffer.size(), file);
+    return readSize == buffer.size() - initialSize;
+}
+
+static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer)
+{
+    FILE* f = fopen(fileName.utf8().data(), "rb");
+    if (!f) {
+        fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
+        return false;
+    }
+
+    bool result = fillBufferWithContentsOfFile(f, buffer);
+    fclose(f);
+
+    return result;
+}
+
+
++ (instancetype)scriptFromUTF8File:(NSURL *)filePath inVirtualMachine:(JSVirtualMachine *)vm withCodeSigning:(NSURL *)codeSigningPath andBytecodeCache:(NSURL *)cachePath
+{
+    // FIXME: This should check codeSigning.
+    UNUSED_PARAM(codeSigningPath);
+    // FIXME: This should actually cache bytecode.
+    UNUSED_PARAM(cachePath);
+    UNUSED_PARAM(vm);
+    URL filePathURL([filePath absoluteURL]);
+    if (!filePathURL.isLocalFile())
+        return nil;
+    // FIXME: This should mmap the contents of the file instead of copying it into dirty memory.
+    Vector<char> buffer;
+    if (!fillBufferWithContentsOfFile(filePathURL.fileSystemPath(), buffer))
+        return nil;
+
+    JSScript *result = [[JSScript alloc] init];
+    result->m_source = String::fromUTF8WithLatin1Fallback(buffer.data(), buffer.size());
+    return result;
+}
+
+const String& getJSScriptSourceCode(JSScript *module) { return module->m_source; }
+
+@end
+
+
+#endif
diff --git a/Source/JavaScriptCore/API/JSScriptInternal.h b/Source/JavaScriptCore/API/JSScriptInternal.h
new file mode 100644 (file)
index 0000000..a973ed1
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#import "JSScript.h"
+#import "SourceCode.h"
+
+OBJC_CLASS JSScript;
+
+const String& getJSScriptSourceCode(JSScript *);
index 4f1a8f6..54b755e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,8 +26,7 @@
 #ifndef JSValueInternal_h
 #define JSValueInternal_h
 
-#import <JavaScriptCore/JavaScriptCore.h>
-#import <JavaScriptCore/JSValue.h>
+#import <JavaScriptCore/JSValuePrivate.h>
 
 #if JSC_OBJC_API_ENABLED
 
index 5ca9a7f..9122ed4 100644 (file)
@@ -46,8 +46,8 @@ JSContextGroupRef getGroupFromVirtualMachine(JSVirtualMachine *);
 
 - (JSContext *)contextForGlobalContextRef:(JSGlobalContextRef)globalContext;
 - (void)addContext:(JSContext *)wrapper forGlobalContextRef:(JSGlobalContextRef)globalContext;
-
 @end
+
 #endif // defined(__OBJC__)
 
 void scanExternalObjectGraph(JSC::VM&, JSC::SlotVisitor&, void* root);
index 37e1385..435fdbb 100644 (file)
@@ -31,7 +31,9 @@
 #import "DFGWorklist.h"
 #import "DateTests.h"
 #import "JSCast.h"
+#import "JSContextPrivate.h"
 #import "JSExportTests.h"
+#import "JSScript.h"
 #import "JSValuePrivate.h"
 #import "JSVirtualMachineInternal.h"
 #import "JSVirtualMachinePrivate.h"
@@ -1812,12 +1814,248 @@ static void parallelPromiseResolveTest()
     }
 }
 
+typedef JSValue *(^ResolveBlock)(JSContext *, JSValue *, JSScript *);
+typedef void (^FetchBlock)(JSContext *, JSValue *, JSValue *, JSValue *);
+
+@interface JSContextFetchDelegate : JSContext <JSModuleLoaderDelegate>
+
++ (instancetype)contextWithBlockForFetch:(FetchBlock)block;
+
+@end
+
+@implementation JSContextFetchDelegate {
+    FetchBlock m_fetchBlock;
+}
+
++ (instancetype)contextWithBlockForFetch:(FetchBlock)block
+{
+    auto *result = [[JSContextFetchDelegate alloc] init];
+    result->m_fetchBlock = block;
+    return result;
+}
+
+- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
+{
+    m_fetchBlock(context, identifier, resolve, reject);
+}
+
+@end
+
+static void checkModuleCodeRan(JSContext *context, JSValue *promise, JSValue *expected)
+{
+    __block BOOL promiseWasResolved = false;
+    [promise invokeMethod:@"then" withArguments:@[^(JSValue *exportValue) {
+        promiseWasResolved = true;
+        checkResult(@"module exported value 'exp' is null", [exportValue[@"exp"] isEqualToObject:expected]);
+        checkResult(@"ran is %@", [context[@"ran"] isEqualToObject:expected]);
+    }, ^(JSValue *error) {
+        NSLog(@"%@", [error toString]);
+        checkResult(@"module graph was resolved as expected", NO);
+    }]];
+    checkResult(@"Promise was resolved", promiseWasResolved);
+}
+
+static void checkModuleWasRejected(JSContext *context, JSValue *promise)
+{
+    __block BOOL promiseWasRejected = false;
+    [promise invokeMethod:@"then" withArguments:@[^() {
+        checkResult(@"module was rejected as expected", NO);
+    }, ^(JSValue *error) {
+        promiseWasRejected = true;
+        NSLog(@"%@", [error toString]);
+        checkResult(@"module graph was rejected with error", ![error isEqualWithTypeCoercionToObject:[JSValue valueWithNullInContext:context]]);
+    }]];
+}
+
+static void testFetch()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext *context, JSValue *identifier, JSValue *resolve, JSValue *reject) {
+            if ([identifier isEqualToObject:@"file:///directory/bar.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"import \"../foo.js\"; export let exp = null;" inVirtualMachine:[context virtualMachine]]]];
+            else if ([identifier isEqualToObject:@"file:///foo.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"globalThis.ran = null;" inVirtualMachine:[context virtualMachine]]]];
+            else
+                [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Weird path" inContext:context]]];
+        }];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('./bar.js');" withSourceURL:[NSURL fileURLWithPath:@"/directory" isDirectory:YES]];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+        checkModuleCodeRan(context, promise, null);
+    }
+}
+
+static void testFetchWithTwoCycle()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext *context, JSValue *identifier, JSValue *resolve, JSValue *reject) {
+            if ([identifier isEqualToObject:@"file:///directory/bar.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"import { n } from \"../foo.js\"; export let exp = n;" inVirtualMachine:[context virtualMachine]]]];
+            else if ([identifier isEqualToObject:@"file:///foo.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"import \"directory/bar.js\"; globalThis.ran = null; export let n = null;" inVirtualMachine:[context virtualMachine]]]];
+            else
+                [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Weird path" inContext:context]]];
+        }];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('./bar.js');" withSourceURL:[NSURL fileURLWithPath:@"/directory" isDirectory:YES]];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+        checkModuleCodeRan(context, promise, null);
+    }
+}
+
+
+static void testFetchWithThreeCycle()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext *context, JSValue *identifier, JSValue *resolve, JSValue *reject) {
+            if ([identifier isEqualToObject:@"file:///directory/bar.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"import { n } from \"../foo.js\"; export let foo = n;" inVirtualMachine:[context virtualMachine]]]];
+            else if ([identifier isEqualToObject:@"file:///foo.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"import \"otherDirectory/baz.js\"; export let n = null;" inVirtualMachine:[context virtualMachine]]]];
+            else if ([identifier isEqualToObject:@"file:///otherDirectory/baz.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"import { foo } from \"../directory/bar.js\"; globalThis.ran = null; export let exp = foo;" inVirtualMachine:[context virtualMachine]]]];
+            else
+                [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Weird path" inContext:context]]];
+        }];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('../otherDirectory/baz.js');" withSourceURL:[NSURL fileURLWithPath:@"/directory" isDirectory:YES]];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+        checkModuleCodeRan(context, promise, null);
+    }
+}
+
+static void testLoaderResolvesAbsoluteScriptURL()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext *context, JSValue *identifier, JSValue *resolve, JSValue *reject) {
+            if ([identifier isEqualToObject:@"file:///directory/bar.js"])
+                [resolve callWithArguments:@[[JSScript scriptWithSource:@"export let exp = null; globalThis.ran = null;" inVirtualMachine:[context virtualMachine]]]];
+            else
+                [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Weird path" inContext:context]]];
+        }];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('/directory/bar.js');"];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+        checkModuleCodeRan(context, promise, null);
+    }
+}
+
+static void testLoaderRejectsNilScriptURL()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext *, JSValue *, JSValue *, JSValue *) {
+            checkResult(@"Code is not run", NO);
+        }];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('../otherDirectory/baz.js');"];
+        checkModuleWasRejected(context, promise);
+    }
+}
+
+static void testLoaderRejectsFailedFetch()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext *context, JSValue *, JSValue *, JSValue *reject) {
+            [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Nope" inContext:context]]];
+        }];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('/otherDirectory/baz.js');"];
+        checkModuleWasRejected(context, promise);
+    }
+}
+
+static void testImportModuleTwice()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFetchDelegate contextWithBlockForFetch:^(JSContext * context, JSValue *, JSValue *resolve, JSValue *) {
+            [resolve callWithArguments:@[[JSScript scriptWithSource:@"ran++; export let exp = 1;" inVirtualMachine:[context virtualMachine]]]];
+        }];
+        context.moduleLoaderDelegate = context;
+        context[@"ran"] = @(0);
+        JSValue *promise = [context evaluateScript:@"import('/baz.js');"];
+        JSValue *promise2 = [context evaluateScript:@"import('/baz.js');"];
+        JSValue *one = [JSValue valueWithInt32:1 inContext:context];
+        checkModuleCodeRan(context, promise, one);
+        checkModuleCodeRan(context, promise2, one);
+    }
+}
+
+@interface JSContextFileLoaderDelegate : JSContext <JSModuleLoaderDelegate>
+
++ (instancetype)newContext;
+
+@end
+
+@implementation JSContextFileLoaderDelegate {
+}
+
++ (instancetype)newContext
+{
+    auto *result = [[JSContextFileLoaderDelegate alloc] init];
+    return result;
+}
+
+static NSURL *resolvePathToScripts()
+{
+    NSString *arg0 = NSProcessInfo.processInfo.arguments[0];
+    NSURL *base;
+    if ([arg0 hasPrefix:@"/"])
+        base = [NSURL fileURLWithPath:arg0 isDirectory:NO];
+    else {
+        const size_t maxLength = 10000;
+        char cwd[maxLength];
+        if (!getcwd(cwd, maxLength)) {
+            NSLog(@"getcwd errored with code: %s", strerror(errno));
+            exit(1);
+        }
+        NSURL *cwdURL = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%s", cwd]];
+        base = [NSURL fileURLWithPath:arg0 isDirectory:NO relativeToURL:cwdURL];
+    }
+    return [NSURL fileURLWithPath:@"./testapiScripts/" isDirectory:YES relativeToURL:base];
+}
+
+- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
+{
+    NSURL *filePath = [NSURL URLWithString:[identifier toString]];
+    auto *script = [JSScript scriptFromUTF8File:filePath inVirtualMachine:[context virtualMachine] withCodeSigning:nil andBytecodeCache:nil];
+    if (script)
+        [resolve callWithArguments:@[script]];
+    else
+        [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Unable to create Script" inContext:context]]];
+}
+
+@end
+
+static void testLoadBasicFile()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFileLoaderDelegate newContext];
+        context.moduleLoaderDelegate = context;
+        JSValue *promise = [context evaluateScript:@"import('./basic.js');" withSourceURL:resolvePathToScripts()];
+        JSValue *null = [JSValue valueWithNullInContext:context];
+        checkModuleCodeRan(context, promise, null);
+    }
+}
+
 void testObjectiveCAPI()
 {
     NSLog(@"Testing Objective-C API");
+
     checkNegativeNSIntegers();
     runJITThreadLimitTests();
 
+    testLoaderResolvesAbsoluteScriptURL();
+    testFetch();
+    testFetchWithTwoCycle();
+    testFetchWithThreeCycle();
+    testImportModuleTwice();
+
+    testLoaderRejectsNilScriptURL();
+    testLoaderRejectsFailedFetch();
+
+    // File loading
+    testLoadBasicFile();
+
     promiseWithExecutor(Resolution::ResolveEager);
     promiseWithExecutor(Resolution::RejectEager);
     promiseWithExecutor(Resolution::ResolveLate);
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/basic.js b/Source/JavaScriptCore/API/tests/testapiScripts/basic.js
new file mode 100644 (file)
index 0000000..8528c49
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+export let exp = null;
+globalThis.ran = null;
index 625178f..d0a5832 100644 (file)
@@ -1,3 +1,91 @@
+2019-01-14  Keith Miller  <keith_miller@apple.com>
+
+        JSC should have a module loader API
+        https://bugs.webkit.org/show_bug.cgi?id=191121
+
+        Reviewed by Michael Saboff.
+
+        This patch adds a new delegate to JSContext that is called to fetch
+        any resolved module. The resolution of a module identifier is computed
+        as if it were a URL on the web with the caveat that it must be a file URL.
+
+        A new class JSScript has also been added that is similar to JSScriptRef.
+        Right now all JSScripts are copied into memory. In the future we should
+        mmap the provided file into memory so the OS can evict it to disk under
+        pressure. Additionally, the API does not make use of the code signing path
+        nor the bytecode caching path, which we will add in subsequent patches.
+
+        Lastly, a couple of new convenience methods have been added. C API
+        conversion, can now toRef a JSValue with just a vm rather than
+        requiring an ExecState. Secondly, there is now a call wrapper that
+        does not require CallData and CallType since many places don't
+        care about this.
+
+        * API/APICast.h:
+        (toRef):
+        * API/JSAPIGlobalObject.cpp: Copied from Source/JavaScriptCore/API/JSVirtualMachineInternal.h.
+        * API/JSAPIGlobalObject.h: Added.
+        (JSC::JSAPIGlobalObject::create):
+        (JSC::JSAPIGlobalObject::createStructure):
+        (JSC::JSAPIGlobalObject::JSAPIGlobalObject):
+        * API/JSAPIGlobalObject.mm: Added.
+        (JSC::JSAPIGlobalObject::moduleLoaderResolve):
+        (JSC::JSAPIGlobalObject::moduleLoaderImportModule):
+        (JSC::JSAPIGlobalObject::moduleLoaderFetch):
+        (JSC::JSAPIGlobalObject::moduleLoaderCreateImportMetaProperties):
+        * API/JSAPIValueWrapper.h:
+        (JSC::jsAPIValueWrapper): Deleted.
+        * API/JSContext.h:
+        * API/JSContext.mm:
+        (-[JSContext moduleLoaderDelegate]):
+        (-[JSContext setModuleLoaderDelegate:]):
+        * API/JSContextInternal.h:
+        * API/JSContextPrivate.h:
+        * API/JSContextRef.cpp:
+        (JSGlobalContextCreateInGroup):
+        * API/JSScript.h: Added.
+        * API/JSScript.mm: Added.
+        (+[JSScript scriptWithSource:inVirtualMachine:]):
+        (fillBufferWithContentsOfFile):
+        (+[JSScript scriptFromUTF8File:inVirtualMachine:withCodeSigning:andBytecodeCache:]):
+        (getJSScriptSourceCode):
+        * API/JSScriptInternal.h: Copied from Source/JavaScriptCore/API/JSVirtualMachineInternal.h.
+        * API/JSValueInternal.h:
+        * API/JSVirtualMachineInternal.h:
+        * API/tests/testapi.mm:
+        (+[JSContextFetchDelegate contextWithBlockForFetch:]):
+        (-[JSContextFetchDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
+        (checkModuleCodeRan):
+        (checkModuleWasRejected):
+        (testFetch):
+        (testFetchWithTwoCycle):
+        (testFetchWithThreeCycle):
+        (testLoaderResolvesAbsoluteScriptURL):
+        (testLoaderRejectsNilScriptURL):
+        (testLoaderRejectsFailedFetch):
+        (testImportModuleTwice):
+        (+[JSContextFileLoaderDelegate newContext]):
+        (resolvePathToScripts):
+        (-[JSContextFileLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
+        (testLoadBasicFile):
+        (testObjectiveCAPI):
+        * API/tests/testapiScripts/basic.js: Copied from Source/JavaScriptCore/API/JSVirtualMachineInternal.h.
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * SourcesCocoa.txt:
+        * config.h:
+        * postprocess-headers.sh:
+        * runtime/CallData.cpp:
+        (JSC::call):
+        * runtime/CallData.h:
+        * runtime/Completion.cpp:
+        (JSC::loadAndEvaluateModule):
+        * runtime/Completion.h:
+        * runtime/JSCast.h:
+        (JSC::jsSecureCast):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::createProxyProperty):
+
 2019-01-14  Dominik Infuehr  <dinfuehr@igalia.com>
 
         Fix property access on ARM with the baseline JIT
index 22163bf..19c24ca 100644 (file)
                539FB8BA1C99DA7C00940FA1 /* JSArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */; };
                53B4BD121F68B32500D2BEA3 /* WasmOps.h in Headers */ = {isa = PBXBuildFile; fileRef = 533B15DE1DC7F463004D500A /* WasmOps.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53B601EC2034B8C5006BE667 /* JSCast.h in Headers */ = {isa = PBXBuildFile; fileRef = 53B601EB2034B8C5006BE667 /* JSCast.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               53C3D5E521ECE7720087FDFC /* basic.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 53C3D5E421ECE6CE0087FDFC /* basic.js */; };
+               53C4F66B21B1A409002FD009 /* JSAPIGlobalObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 53C4F66A21B1A409002FD009 /* JSAPIGlobalObject.h */; };
                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 */; };
                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, ); }; };
                53EAFE2F208DFAB4007D524B /* testapi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 531D4E191F59CDD200EC836C /* testapi.cpp */; };
+               53EE01B6218F691600AD1F8D /* JSScript.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EE01B5218F690F00AD1F8D /* JSScript.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               53EE01B8218F7EFF00AD1F8D /* JSScriptInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EE01B7218F7EFF00AD1F8D /* JSScriptInternal.h */; };
                53F11F41209138D700E411A7 /* JSImmutableButterfly.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F11F40209138D700E411A7 /* JSImmutableButterfly.h */; };
                53F40E851D58F9770099A1B6 /* WasmSections.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E841D58F9770099A1B6 /* WasmSections.h */; };
                53F40E8B1D5901BB0099A1B6 /* WasmFunctionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */; };
                5DBB1511131D0B130056AD36 /* Copy Support Script */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 12;
-                       dstPath = "";
+                       dstPath = testapiScripts;
                        dstSubfolderSpec = 16;
                        files = (
+                               53C3D5E521ECE7720087FDFC /* basic.js in Copy Support Script */,
                                FECB8B2A1D25CB5A006F2463 /* testapi-function-overrides.js in Copy Support Script */,
                                5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */,
                        );
                53B0BE351E561B0900A8FC29 /* ProxyableAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProxyableAccessCase.cpp; sourceTree = "<group>"; };
                53B0BE371E561B2400A8FC29 /* IntrinsicGetterAccessCase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntrinsicGetterAccessCase.cpp; sourceTree = "<group>"; };
                53B601EB2034B8C5006BE667 /* JSCast.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSCast.h; sourceTree = "<group>"; };
+               53C3D5E421ECE6CE0087FDFC /* basic.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = basic.js; sourceTree = "<group>"; };
+               53C3D5E621ED1C050087FDFC /* JSAPIGlobalObject.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSAPIGlobalObject.cpp; sourceTree = "<group>"; };
+               53C4F66A21B1A409002FD009 /* JSAPIGlobalObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSAPIGlobalObject.h; sourceTree = "<group>"; };
+               53C4F66C21B1A7CA002FD009 /* JSAPIGlobalObject.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JSAPIGlobalObject.mm; sourceTree = "<group>"; };
                53C6FEEE1E8ADFA900B18425 /* WasmOpcodeOrigin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmOpcodeOrigin.h; sourceTree = "<group>"; };
                53C6FEF01E8AFE0C00B18425 /* WasmOpcodeOrigin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmOpcodeOrigin.cpp; sourceTree = "<group>"; };
                53CA73071EA533D80076049D /* WasmBBQPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmBBQPlan.cpp; sourceTree = "<group>"; };
                53E9E0A91EAE83DE00FEE251 /* WasmMachineThreads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmMachineThreads.cpp; sourceTree = "<group>"; };
                53E9E0AA1EAE83DE00FEE251 /* WasmMachineThreads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmMachineThreads.h; sourceTree = "<group>"; };
                53E9E0AE1EAEC45700FEE251 /* WasmTierUpCount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmTierUpCount.h; sourceTree = "<group>"; };
+               53EE01B4218F690E00AD1F8D /* JSScript.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = JSScript.mm; sourceTree = "<group>"; };
+               53EE01B5218F690F00AD1F8D /* JSScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSScript.h; sourceTree = "<group>"; };
+               53EE01B7218F7EFF00AD1F8D /* JSScriptInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSScriptInternal.h; sourceTree = "<group>"; };
                53F11F40209138D700E411A7 /* JSImmutableButterfly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSImmutableButterfly.h; sourceTree = "<group>"; };
                53F11F422091749800E411A7 /* JSImmutableButterfly.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSImmutableButterfly.cpp; sourceTree = "<group>"; };
                53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTypedArrayViewPrototype.cpp; sourceTree = "<group>"; };
                141211000A48772600480255 /* tests */ = {
                        isa = PBXGroup;
                        children = (
+                               53C3D5E321ECE68E0087FDFC /* testapiScripts */,
                                FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */,
                                FEF040521AAEC4ED00BD28B0 /* CompareAndSwapTest.h */,
                                C29ECB021804D0ED00D2CBB4 /* CurrentThisInsideBlockGetterTest.h */,
                                53529A4B1C457B75000B49C6 /* APIUtils.h */,
                                1CAA8B4A0D32C39A0041BCFF /* JavaScript.h */,
                                1CAA8B4B0D32C39A0041BCFF /* JavaScriptCore.h */,
+                               53C3D5E621ED1C050087FDFC /* JSAPIGlobalObject.cpp */,
+                               53C4F66A21B1A409002FD009 /* JSAPIGlobalObject.h */,
+                               53C4F66C21B1A7CA002FD009 /* JSAPIGlobalObject.mm */,
                                BC0894D50FAFBA2D00001865 /* JSAPIValueWrapper.cpp */,
                                BC0894D60FAFBA2D00001865 /* JSAPIValueWrapper.h */,
                                C2CF39C016E15A8100DD69BE /* JSAPIWrapperObject.h */,
                                A552C37D1ADDB8FE00139726 /* JSRemoteInspector.cpp */,
                                A552C37E1ADDB8FE00139726 /* JSRemoteInspector.h */,
                                95C18D3E0C90E7EF00E72F73 /* JSRetainPtr.h */,
+                               53EE01B5218F690F00AD1F8D /* JSScript.h */,
+                               53EE01B4218F690E00AD1F8D /* JSScript.mm */,
+                               53EE01B7218F7EFF00AD1F8D /* JSScriptInternal.h */,
                                A7C0C4AA167C08CD0017011D /* JSScriptRef.cpp */,
                                A7C0C4AB167C08CD0017011D /* JSScriptRefPrivate.h */,
                                1482B74C0A43032800517CFC /* JSStringRef.cpp */,
                        path = "unified-sources";
                        sourceTree = "<group>";
                };
+               53C3D5E321ECE68E0087FDFC /* testapiScripts */ = {
+                       isa = PBXGroup;
+                       children = (
+                               53C3D5E421ECE6CE0087FDFC /* basic.js */,
+                       );
+                       name = testapiScripts;
+                       path = API/tests/testapiScripts;
+                       sourceTree = "<group>";
+               };
                650FDF8D09D0FCA700769E54 /* Derived Sources */ = {
                        isa = PBXGroup;
                        children = (
                                0F5EF91F16878F7D003E5C25 /* JITThunks.h in Headers */,
                                0FC712E317CD8793008CC93C /* JITToDFGDeferredCompilationCallback.h in Headers */,
                                DC0184191D10C1890057B053 /* JITWorklist.h in Headers */,
+                               53C4F66B21B1A409002FD009 /* JSAPIGlobalObject.h in Headers */,
                                840480131021A1D9008E7F01 /* JSAPIValueWrapper.h in Headers */,
                                C2CF39C216E15A8100DD69BE /* JSAPIWrapperObject.h in Headers */,
                                BC18C4170E16F5CD00B34460 /* JSArray.h in Headers */,
                                BC18C4260E16F5CD00B34460 /* JSRetainPtr.h in Headers */,
                                534638711E70CF3D00F12AC1 /* JSRunLoopTimer.h in Headers */,
                                14874AE615EBDE4A002E3587 /* JSScope.h in Headers */,
+                               53EE01B6218F691600AD1F8D /* JSScript.h in Headers */,
                                9064337DD4B0402BAF34A592 /* JSScriptFetcher.h in Headers */,
                                E3201C1D1F8E82360076A032 /* JSScriptFetchParameters.h in Headers */,
+                               53EE01B8218F7EFF00AD1F8D /* JSScriptInternal.h in Headers */,
                                A7C0C4AC168103020017011D /* JSScriptRefPrivate.h in Headers */,
                                0F919D11157F332C004A4E7D /* JSSegmentedVariableObject.h in Headers */,
                                0F4F82881E2FFDE00075184C /* JSSegmentedVariableObjectHeapCellType.h in Headers */,
index a2332b7..38791ac 100644 (file)
@@ -21,6 +21,7 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 // THE POSSIBILITY OF SUCH DAMAGE.
 
+API/JSAPIGlobalObject.cpp
 API/JSAPIValueWrapper.cpp
 API/JSBase.cpp
 API/JSCTestRunnerUtils.cpp
index acf4fdb..af6f9ef 100644 (file)
@@ -21,7 +21,9 @@
 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 // THE POSSIBILITY OF SUCH DAMAGE.
 
+API/JSAPIGlobalObject.mm
 API/JSAPIWrapperObject.mm
+API/JSScript.mm
 API/JSContext.mm
 API/JSManagedValue.mm
 API/JSRemoteInspector.cpp
index e0232cf..e364390 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2019 Apple Inc. All rights reserved.
  * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
@@ -24,6 +24,7 @@
 #endif
 
 #define JSC_API_AVAILABLE(...)
+#define JSC_CLASS_AVAILABLE(...) JS_EXPORT
 // Use zero since it will be less than any possible version number.
 #define JSC_MAC_VERSION_TBA 0
 #define JSC_IOS_VERSION_TBA 0
index fd28836..107ae1b 100755 (executable)
@@ -72,8 +72,8 @@ function rewrite_headers () {
             -e s/JSC_IOS_VERSION_TBA/${IOS_VERSION_NUMBER}/g
             -e s/JSC_API_AVAILABLE/API_AVAILABLE/
             -e s/JSC_API_DEPRECATED/API_DEPRECATED/
-            -e "s/^JSC_CLASS_AVAILABLE/JSC_EXTERN API_AVAILABLE/"
-            -e "s/^JSC_CLASS_DEPRECATED/JSC_EXTERN API_DEPRECATED/"
+            -e "s/^JSC_CLASS_AVAILABLE/JS_EXPORT API_AVAILABLE/"
+            -e "s/^JSC_CLASS_DEPRECATED/JS_EXPORT API_DEPRECATED/"
         )
     else
         SED_OPTIONS+=(
index c8d08ec..1383a54 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2018 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008-2019 Apple Inc. All Rights Reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 namespace JSC {
 
+JSValue call(ExecState* exec, JSValue functionObject, const ArgList& args, const char* errorMessage)
+{
+    return call(exec, functionObject, functionObject, args, errorMessage);
+}
+
+JSValue call(ExecState* exec, JSValue functionObject, JSValue thisValue, const ArgList& args, const char* errorMessage)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    CallData callData;
+    CallType callType = getCallData(vm, functionObject, callData);
+    if (callType == CallType::None)
+        return throwTypeError(exec, scope, errorMessage);
+
+    RELEASE_AND_RETURN(scope, call(exec, functionObject, callType, callData, thisValue, args));
+}
+
 JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args)
 {
     VM& vm = exec->vm();
index cbf9167..08f79df 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -64,6 +64,10 @@ enum class ProfilingReason {
     Other
 };
 
+// Convenience wrapper so you don't need to deal with CallData and CallType unless you are going to use them.
+JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, const ArgList&, const char* errorMessage);
+JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, JSValue thisValue, const ArgList&, const char* errorMessage);
+
 JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&);
 JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&, NakedPtr<Exception>& returnedException);
 
index 1102f66..3fb4ab9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003-2018 Apple Inc.
+ *  Copyright (C) 2003-2019 Apple Inc.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -161,6 +161,16 @@ static JSInternalPromise* rejectPromise(ExecState* exec, JSGlobalObject* globalO
     return deferred->promise();
 }
 
+JSInternalPromise* loadAndEvaluateModule(ExecState* exec, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher)
+{
+    VM& vm = exec->vm();
+    JSLockHolder lock(vm);
+    RELEASE_ASSERT(vm.atomicStringTable() == Thread::current().atomicStringTable());
+    RELEASE_ASSERT(!vm.isCollectorBusyOnCurrentThread());
+
+    return vm.vmEntryGlobalObject(exec)->moduleLoader()->loadAndEvaluateModule(exec, moduleId, parameters, scriptFetcher);
+}
+
 JSInternalPromise* loadAndEvaluateModule(ExecState* exec, const String& moduleName, JSValue parameters, JSValue scriptFetcher)
 {
     VM& vm = exec->vm();
index de448a4..827e61f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
- *  Copyright (C) 2003, 2007 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003-2019 Apple Inc. All rights reserved.
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Library General Public
@@ -33,6 +33,7 @@ class ExecState;
 class JSObject;
 class ParserError;
 class SourceCode;
+class Symbol;
 class VM;
 class JSInternalPromise;
 
@@ -57,6 +58,7 @@ inline JSValue profiledEvaluate(ExecState* exec, ProfilingReason reason, const S
 JS_EXPORT_PRIVATE JSValue evaluateWithScopeExtension(ExecState*, const SourceCode&, JSObject* scopeExtension, NakedPtr<Exception>& returnedException);
 
 // Load the module source and evaluate it.
+JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, Symbol* moduleId, JSValue parameters, JSValue scriptFetcher);
 JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, const String& moduleName, JSValue parameters, JSValue scriptFetcher);
 JS_EXPORT_PRIVATE JSInternalPromise* loadAndEvaluateModule(ExecState*, const SourceCode&, JSValue scriptFetcher);
 
index a369d48..495c7e1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2018-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -165,4 +165,12 @@ To jsDynamicCast(VM& vm, JSValue from)
     return jsDynamicCast<To>(vm, from.asCell());
 }
 
+template<typename To, typename From>
+To jsSecureCast(VM& vm, From from)
+{
+    auto* result = jsDynamicCast<To>(vm, from);
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(result);
+    return result;
+}
+
 }
index e13707a..bb70cf5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2018 Apple Inc. All rights reserved.
+ * Copyright (C) 2007-2019 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Cameron Zwarich (cwzwarich@uwaterloo.ca)
  *
  * Redistribution and use in source and binary forms, with or without
@@ -63,6 +63,7 @@
 #include "Error.h"
 #include "ErrorConstructor.h"
 #include "ErrorPrototype.h"
+#include "Exception.h"
 #include "FunctionConstructor.h"
 #include "FunctionPrototype.h"
 #include "GeneratorFunctionConstructor.h"
 
 namespace JSC {
 
-    static JSValue createProxyProperty(VM& vm, JSObject* object)
+static JSValue createProxyProperty(VM& vm, JSObject* object)
 {
     JSGlobalObject* global = jsCast<JSGlobalObject*>(object);
     return ProxyConstructor::create(vm, ProxyConstructor::createStructure(vm, global, global->functionPrototype()));