Add API to get all the dependencies of a given JSScript
https://bugs.webkit.org/show_bug.cgi?id=199746

Reviewed by Saam Barati.

The method only returns the dependencies if the module was
actually evaluated. Technically, we know what the dependencies are
at the satisfy phase but for API simplicity we only provide that
information if the module graph was complete enough to at least
run.

This patch also fixes an issue where we would allow import
specifiers that didn't start "./" or "/". For reference, We have
this restriction to be consistent with the web/node. The
restriction exists in order to preserve namespace for
builtin-modules.

Lastly, this patch makes it so that we copy all scripts in the
API/tests/testapiScripts directory so they don't have to be
individually added to the xcode project.

* API/JSAPIGlobalObject.mm:
(JSC::computeValidImportSpecifier):
(JSC::JSAPIGlobalObject::moduleLoaderResolve):
(JSC::JSAPIGlobalObject::moduleLoaderImportModule):
* API/JSContext.mm:
(-[JSContext dependencyIdentifiersForModuleJSScript:]):
* API/JSContextPrivate.h:
* API/JSScript.h:
* API/tests/testapi.mm:
(testFetchWithTwoCycle):
(testFetchWithThreeCycle):
(testModuleBytecodeCache):
(+[JSContextFileLoaderDelegate newContext]):
(-[JSContextFileLoaderDelegate fetchModuleScript:]):
(-[JSContextFileLoaderDelegate findScriptForKey:]):
(-[JSContextFileLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
(testDependenciesArray):
(testDependenciesEvaluationError):
(testDependenciesSyntaxError):
(testDependenciesBadImportId):
(testDependenciesMissingImport):
(testObjectiveCAPI):
* API/tests/testapiScripts/dependencyListTests/badModuleImportId.js: Added.
* API/tests/testapiScripts/dependencyListTests/bar.js: Added.
* API/tests/testapiScripts/dependencyListTests/dependenciesEntry.js: Added.
* API/tests/testapiScripts/dependencyListTests/foo.js: Added.
* API/tests/testapiScripts/dependencyListTests/missingImport.js: Added.
* API/tests/testapiScripts/dependencyListTests/referenceError.js: Added.
* API/tests/testapiScripts/dependencyListTests/syntaxError.js: Added.
* API/tests/testapiScripts/testapi-function-overrides.js: Renamed from Source/JavaScriptCore/API/tests/testapi-function-overrides.js.
* API/tests/testapiScripts/testapi.js: Renamed from Source/JavaScriptCore/API/tests/testapi.js.
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/ModuleLoader.js:
(dependencyKeysIfEvaluated):
* runtime/JSModuleLoader.cpp:
(JSC::JSModuleLoader::dependencyKeysIfEvaluated):
* runtime/JSModuleLoader.h:
* shell/CMakeLists.txt:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@247403 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/API/JSAPIGlobalObject.mm b/Source/JavaScriptCore/API/JSAPIGlobalObject.mm
index 5332280..116109b 100644
--- a/Source/JavaScriptCore/API/JSAPIGlobalObject.mm
+++ b/Source/JavaScriptCore/API/JSAPIGlobalObject.mm
@@ -69,6 +69,33 @@
     nullptr, // instantiateStreaming
 };
 
+static Expected<URL, String> computeValidImportSpecifier(const URL& base, const String& specifier)
+{
+    URL absoluteURL(URL(), specifier);
+    if (absoluteURL.isValid())
+        return absoluteURL;
+
+    if (!specifier.startsWith('/') && !specifier.startsWith("./") && !specifier.startsWith("../"))
+        return makeUnexpected(makeString("Module specifier: "_s, specifier, " does not start with \"/\", \"./\", or \"../\"."_s));
+
+    if (specifier.startsWith('/')) {
+        absoluteURL = URL(URL({ }, "file://"), specifier);
+        if (absoluteURL.isValid())
+            return absoluteURL;
+    }
+
+    if (base == URL())
+        return makeUnexpected("Could not determine the base URL for loading."_s);
+
+    if (!base.isValid())
+        return makeUnexpected(makeString("Referrering script's url is not valid: "_s, base.string()));
+
+    absoluteURL = URL(base, specifier);
+    if (absoluteURL.isValid())
+        return absoluteURL;
+    return makeUnexpected(makeString("Could not form valid URL from identifier and base. Tried:"_s, absoluteURL.string()));
+}
+
 Identifier JSAPIGlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue referrer, JSValue)
 {
     VM& vm = exec->vm();
@@ -76,23 +103,22 @@
     ASSERT_UNUSED(globalObject, globalObject == exec->lexicalGlobalObject());
     ASSERT(key.isString() || key.isSymbol());
     String name =  key.toWTFString(exec);
+    RETURN_IF_EXCEPTION(scope, { });
 
+    URL base;
     if (JSString* referrerString = jsDynamicCast<JSString*>(vm, referrer)) {
         String value = referrerString->value(exec);
-        URL referrerURL({ }, value);
         RETURN_IF_EXCEPTION(scope, { });
+        URL referrerURL({ }, value);
         RELEASE_ASSERT(referrerURL.isValid());
-
-        URL url = URL(referrerURL, name);
-        if (url.isValid())
-            return Identifier::fromString(exec, url);
-    } else {
-        URL url = URL({ }, name);
-        if (url.isValid())
-            return Identifier::fromString(exec, url);
+        base = WTFMove(referrerURL);
     }
 
-    throwVMError(exec, scope, "Could not form valid URL from identifier and base"_s);
+    auto result = computeValidImportSpecifier(base, name);
+    if (result)
+        return Identifier::fromString(&vm, result.value());
+
+    throwVMError(exec, scope, createError(exec, result.error()));
     return { };
 }
 
@@ -121,33 +147,12 @@
         return reject(exception);
     }
 
-    URL absoluteURL(URL(), specifier);
-    if (absoluteURL.isValid())
-        return import(absoluteURL);
-
-    if (!specifier.startsWith('/') && !specifier.startsWith("./") && !specifier.startsWith("../"))
-        return reject(createError(exec, makeString("Module specifier: ", 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, makeString(noBaseErrorMessage, " Referring script has no URL."_s)));
-
-    auto referrer = sourceOrigin.string();
+    String referrer = !sourceOrigin.isNull() ? sourceOrigin.string() : String();
     URL baseURL(URL(), referrer);
-    if (!baseURL.isValid())
-        return reject(createError(exec, makeString(noBaseErrorMessage, " Referring script's URL is not valid: "_s, baseURL.string())));
-
-    URL url(baseURL, specifier);
-    if (!url.isValid())
-        return reject(createError(exec, makeString("could not determine a valid URL for module specifier. Tried: "_s, url.string())));
-
-    return import(url);
+    auto result = computeValidImportSpecifier(baseURL, specifier);
+    if (result)
+        return import(result.value());
+    return reject(createError(exec, result.error()));
 }
 
 JSInternalPromise* JSAPIGlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue, JSValue)
diff --git a/Source/JavaScriptCore/API/JSContext.mm b/Source/JavaScriptCore/API/JSContext.mm
index 41cae4f..05de4f3 100644
--- a/Source/JavaScriptCore/API/JSContext.mm
+++ b/Source/JavaScriptCore/API/JSContext.mm
@@ -146,6 +146,32 @@
     return [JSValue valueWithJSValueRef:toRef(vm, result) inContext:self];
 }
 
+- (JSValue *)dependencyIdentifiersForModuleJSScript:(JSScript *)script
+{
+    JSC::ExecState* exec = toJS(m_context);
+    JSC::VM& vm = exec->vm();
+    JSC::JSLockHolder locker(vm);
+
+    if (script.type != kJSScriptTypeModule) {
+        self.exceptionHandler(self, [JSValue valueWithNewErrorFromMessage:@"script is not a module" inContext:self]);
+        return [JSValue valueWithUndefinedInContext:self];
+    }
+
+    auto scope = DECLARE_CATCH_SCOPE(vm);
+    JSC::JSArray* result = exec->lexicalGlobalObject()->moduleLoader()->dependencyKeysIfEvaluated(exec, JSC::jsString(&vm, [[script sourceURL] absoluteString]));
+    if (scope.exception()) {
+        JSValueRef exceptionValue = toRef(exec, scope.exception()->value());
+        scope.clearException();
+        return [self valueFromNotifyException:exceptionValue];
+    }
+
+    if (!result) {
+        self.exceptionHandler(self, [JSValue valueWithNewErrorFromMessage:@"script has not run in context or was not evaluated successfully" inContext:self]);
+        return [JSValue valueWithUndefinedInContext:self];
+    }
+    return [JSValue valueWithJSValueRef:toRef(vm, result) inContext:self];
+}
+
 - (void)setException:(JSValue *)value
 {
     JSC::ExecState* exec = toJS(m_context);
diff --git a/Source/JavaScriptCore/API/JSContextPrivate.h b/Source/JavaScriptCore/API/JSContextPrivate.h
index f4656b9..75f526b 100644
--- a/Source/JavaScriptCore/API/JSContextPrivate.h
+++ b/Source/JavaScriptCore/API/JSContextPrivate.h
@@ -84,7 +84,7 @@
 @property (setter=_setDebuggerRunLoop:) CFRunLoopRef _debuggerRunLoop JSC_API_AVAILABLE(macos(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(macos(JSC_MAC_TBA), ios(JSC_IOS_TBA));
+@property (nonatomic, weak) id <JSModuleLoaderDelegate> moduleLoaderDelegate JSC_API_AVAILABLE(macos(10.15), ios(13.0));
 
 /*!
  @method
@@ -94,7 +94,17 @@
 
  Otherwise, if the script was created with kJSScriptTypeModule, the module will be run asynchronously and will return a promise resolved when the module and any transitive dependencies are loaded. The module loader will treat the script as if it had been returned from a delegate call to moduleLoaderDelegate. This mirrors the JavaScript dynamic import operation.
  */
-- (JSValue *)evaluateJSScript:(JSScript *)script;
+// FIXME: Before making this public need to fix: https://bugs.webkit.org/show_bug.cgi?id=199714
+- (JSValue *)evaluateJSScript:(JSScript *)script JSC_API_AVAILABLE(macos(10.15), ios(13.0));
+
+/*!
+ @method
+ @abstract Get the identifiers of the modules a JSScript depends on in this context.
+ @param script the JSScript to determine the dependencies of.
+ @result An Array containing all the identifiers of modules script depends on.
+ @discussion If the provided JSScript was not created with kJSScriptTypeModule, an exception will be thrown. Also, if the script has not already been evaluated in this context an error will be throw.
+ */
+- (JSValue *)dependencyIdentifiersForModuleJSScript:(JSScript *)script JSC_API_AVAILABLE(macos(10.15), ios(13.0));
 
 @end
 
diff --git a/Source/JavaScriptCore/API/JSScript.h b/Source/JavaScriptCore/API/JSScript.h
index d59b0b4..12ccb05 100644
--- a/Source/JavaScriptCore/API/JSScript.h
+++ b/Source/JavaScriptCore/API/JSScript.h
@@ -43,7 +43,7 @@
 };
 
 
-JSC_CLASS_AVAILABLE(macos(JSC_MAC_TBA), ios(JSC_IOS_TBA))
+JSC_CLASS_AVAILABLE(macos(10.15), ios(13.0))
 @interface JSScript : NSObject
 
 /*!
diff --git a/Source/JavaScriptCore/API/tests/testapi.mm b/Source/JavaScriptCore/API/tests/testapi.mm
index 06e8d2b..3ca4cc9 100644
--- a/Source/JavaScriptCore/API/tests/testapi.mm
+++ b/Source/JavaScriptCore/API/tests/testapi.mm
@@ -1946,7 +1946,7 @@
                     error:nil]]];
             } else if ([identifier isEqualToObject:@"file:///foo.js"]) {
                 [resolve callWithArguments:@[[JSScript scriptOfType:kJSScriptTypeModule 
-                    withSource:@"import \"directory/bar.js\"; globalThis.ran = null; export let n = null;"
+                    withSource:@"import \"./directory/bar.js\"; globalThis.ran = null; export let n = null;"
                     andSourceURL:[NSURL fileURLWithPath:@"/foo.js"]
                     andBytecodeCache:nil
                     inVirtualMachine:[context virtualMachine]
@@ -1977,7 +1977,7 @@
                     error:nil]]];
             } else if ([identifier isEqualToObject:@"file:///foo.js"]) {
                 [resolve callWithArguments:@[[JSScript scriptOfType:kJSScriptTypeModule 
-                    withSource:@"import \"otherDirectory/baz.js\"; export let n = null;"
+                    withSource:@"import \"./otherDirectory/baz.js\"; export let n = null;"
                     andSourceURL:[NSURL fileURLWithPath:@"/foo.js"] 
                     andBytecodeCache:nil
                     inVirtualMachine:[context virtualMachine]
@@ -2107,7 +2107,7 @@
 static void testModuleBytecodeCache()
 {
     @autoreleasepool {
-        NSString *fooSource = @"import 'otherDirectory/baz.js'; export let n = null;";
+        NSString *fooSource = @"import './otherDirectory/baz.js'; export let n = null;";
         NSString *barSource = @"import { n } from '../foo.js'; export let foo = () => n;";
         NSString *bazSource = @"import { foo } from '../directory/bar.js'; globalThis.ran = null; export let exp = foo();";
 
@@ -2428,15 +2428,19 @@
 @interface JSContextFileLoaderDelegate : JSContext <JSModuleLoaderDelegate>
 
 + (instancetype)newContext;
+- (JSScript *)fetchModuleScript:(NSString *)relativePath;
 
 @end
 
 @implementation JSContextFileLoaderDelegate {
+    NSMutableDictionary<NSString *, JSScript *> *m_keyToScript;
 }
 
 + (instancetype)newContext
 {
     auto *result = [[JSContextFileLoaderDelegate alloc] init];
+    result.moduleLoaderDelegate = result;
+    result->m_keyToScript = [[NSMutableDictionary<NSString *, JSScript *> alloc] init];
     return result;
 }
 
@@ -2459,18 +2463,45 @@
     return [NSURL fileURLWithPath:@"./testapiScripts/" isDirectory:YES relativeToURL:base];
 }
 
+- (JSScript *)fetchModuleScript:(NSString *)relativePath
+{
+    auto *filePath = [NSURL URLWithString:relativePath relativeToURL:resolvePathToScripts()];
+    if (auto *script = [self findScriptForKey:[filePath absoluteString]])
+        return script;
+    NSError *error;
+    auto *result = [JSScript scriptOfType:kJSScriptTypeModule memoryMappedFromASCIIFile:filePath withSourceURL:filePath andBytecodeCache:nil inVirtualMachine:[self virtualMachine] error:&error];
+    if (!result) {
+        NSLog(@"%@\n", error);
+        CRASH();
+    }
+    [m_keyToScript setObject:result forKey:[filePath absoluteString]];
+    return result;
+}
+
+- (JSScript *)findScriptForKey:(NSString *)key
+{
+    return [m_keyToScript objectForKey:key];
+}
+
 - (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject
 {
     NSURL *filePath = [NSURL URLWithString:[identifier toString]];
+    // FIXME: We should fix this: https://bugs.webkit.org/show_bug.cgi?id=199714
+    if (auto *script = [self findScriptForKey:[identifier toString]]) {
+        [resolve callWithArguments:@[script]];
+        return;
+    }
+
     auto* script = [JSScript scriptOfType:kJSScriptTypeModule
         memoryMappedFromASCIIFile:filePath
         withSourceURL:filePath
         andBytecodeCache:nil 
         inVirtualMachine:context.virtualMachine
         error:nil];
-    if (script)
+    if (script) {
+        [m_keyToScript setObject:script forKey:[identifier toString]];
         [resolve callWithArguments:@[script]];
-    else
+    } else
         [reject callWithArguments:@[[JSValue valueWithNewErrorFromMessage:@"Unable to create Script" inContext:context]]];
 }
 
@@ -2604,6 +2635,111 @@
     }
 }
 
+static void testDependenciesArray()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFileLoaderDelegate newContext];
+
+        JSScript *entryScript = [context fetchModuleScript:@"./dependencyListTests/dependenciesEntry.js"];
+
+        JSValue *promise = [context evaluateJSScript:entryScript];
+        [promise invokeMethod:@"then" withArguments:@[^(JSValue *) {
+            checkResult(@"module ran successfully", true);
+        }, ^(JSValue *) {
+            checkResult(@"module ran successfully", false);
+        }]];
+
+        checkResult(@"looking up the entry script should find the same script again.", [context findScriptForKey:[entryScript.sourceURL absoluteString]] == entryScript);
+
+        auto *deps = [context dependencyIdentifiersForModuleJSScript:entryScript];
+
+        checkResult(@"deps should be an array", [deps isArray]);
+        checkResult(@"deps should have two entries", [deps[@"length"] isEqualToObject:@(2)]);
+
+        checkResult(@"first dependency should be foo.js", [[[[context fetchModuleScript:@"./dependencyListTests/foo.js"] sourceURL] absoluteString] isEqual:[deps[@(0)] toString]]);
+        checkResult(@"second dependency should be bar.js", [[[[context fetchModuleScript:@"./dependencyListTests/bar.js"] sourceURL] absoluteString] isEqual:[deps[@(1)] toString]]);
+    }
+}
+
+static void testDependenciesEvaluationError()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFileLoaderDelegate newContext];
+
+        JSScript *entryScript = [context fetchModuleScript:@"./dependencyListTests/referenceError.js"];
+
+        JSValue *promise = [context evaluateJSScript:entryScript];
+        [promise invokeMethod:@"then" withArguments:@[^(JSValue *) {
+            checkResult(@"module failed successfully", false);
+        }, ^(JSValue *) {
+            checkResult(@"module failed successfully", true);
+        }]];
+
+        auto *deps = [context dependencyIdentifiersForModuleJSScript:entryScript];
+        checkResult(@"deps should be an Array", [deps isArray]);
+        checkResult(@"first dependency should be foo.js", [[[[context fetchModuleScript:@"./dependencyListTests/foo.js"] sourceURL] absoluteString] isEqual:[deps[@(0)] toString]]);
+    }
+}
+
+static void testDependenciesSyntaxError()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFileLoaderDelegate newContext];
+
+        JSScript *entryScript = [context fetchModuleScript:@"./dependencyListTests/syntaxError.js"];
+
+        JSValue *promise = [context evaluateJSScript:entryScript];
+        [promise invokeMethod:@"then" withArguments:@[^(JSValue *) {
+            checkResult(@"module failed successfully", false);
+        }, ^(JSValue *) {
+            checkResult(@"module failed successfully", true);
+        }]];
+
+        auto *deps = [context dependencyIdentifiersForModuleJSScript:entryScript];
+        checkResult(@"deps should be undefined", [deps isUndefined]);
+        checkResult(@"there should be a pending exception on the context", context.exception);
+    }
+}
+
+static void testDependenciesBadImportId()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFileLoaderDelegate newContext];
+
+        JSScript *entryScript = [context fetchModuleScript:@"./dependencyListTests/badModuleImportId.js"];
+
+        JSValue *promise = [context evaluateJSScript:entryScript];
+        [promise invokeMethod:@"then" withArguments:@[^(JSValue *) {
+            checkResult(@"module failed successfully", false);
+        }, ^(JSValue *) {
+            checkResult(@"module failed successfully", true);
+        }]];
+
+        auto *deps = [context dependencyIdentifiersForModuleJSScript:entryScript];
+        checkResult(@"deps should be undefined", [deps isUndefined]);
+        checkResult(@"there should be a pending exception on the context", context.exception);
+    }
+}
+
+static void testDependenciesMissingImport()
+{
+    @autoreleasepool {
+        auto *context = [JSContextFileLoaderDelegate newContext];
+
+        JSScript *entryScript = [context fetchModuleScript:@"./dependencyListTests/missingImport.js"];
+
+        JSValue *promise = [context evaluateJSScript:entryScript];
+        [promise invokeMethod:@"then" withArguments:@[^(JSValue *) {
+            checkResult(@"module failed successfully", false);
+        }, ^(JSValue *) {
+            checkResult(@"module failed successfully", true);
+        }]];
+
+        auto *deps = [context dependencyIdentifiersForModuleJSScript:entryScript];
+        checkResult(@"deps should be undefined", [deps isUndefined]);
+        checkResult(@"there should be a pending exception on the context", context.exception);
+    }
+}
 
 @protocol ToString <JSExport>
 - (NSString *)toString;
@@ -2708,6 +2844,12 @@
     RUN(testLoadBasicFileLegacySPI());
     RUN(testLoadBasicFile());
 
+    RUN(testDependenciesArray());
+    RUN(testDependenciesSyntaxError());
+    RUN(testDependenciesEvaluationError());
+    RUN(testDependenciesBadImportId());
+    RUN(testDependenciesMissingImport());
+
     RUN(promiseWithExecutor(Resolution::ResolveEager));
     RUN(promiseWithExecutor(Resolution::RejectEager));
     RUN(promiseWithExecutor(Resolution::ResolveLate));
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/badModuleImportId.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/badModuleImportId.js
new file mode 100644
index 0000000..7650208
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/badModuleImportId.js
@@ -0,0 +1,3 @@
+
+// This is not a valid identfier for an import so it will fail to import.
+import "sethua"
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/bar.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/bar.js
new file mode 100644
index 0000000..010ffc8
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/bar.js
@@ -0,0 +1 @@
+export let hello = 1;
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/dependenciesEntry.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/dependenciesEntry.js
new file mode 100644
index 0000000..29ce903
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/dependenciesEntry.js
@@ -0,0 +1,2 @@
+import "./foo.js";
+import "./bar.js";
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/foo.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/foo.js
new file mode 100644
index 0000000..b28218a
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/foo.js
@@ -0,0 +1 @@
+export let things = null;
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/missingImport.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/missingImport.js
new file mode 100644
index 0000000..2f53491
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/missingImport.js
@@ -0,0 +1 @@
+import "./file-that-does-not-exist.js"
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/referenceError.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/referenceError.js
new file mode 100644
index 0000000..de55c37
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/referenceError.js
@@ -0,0 +1,3 @@
+import "./foo.js";
+
+tosehuas;
diff --git a/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/syntaxError.js b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/syntaxError.js
new file mode 100644
index 0000000..7b42843
--- /dev/null
+++ b/Source/JavaScriptCore/API/tests/testapiScripts/dependencyListTests/syntaxError.js
@@ -0,0 +1,3 @@
+import "./foo.js";
+
+theoasutas thea tehlr  ebsa;
diff --git a/Source/JavaScriptCore/API/tests/testapi-function-overrides.js b/Source/JavaScriptCore/API/tests/testapiScripts/testapi-function-overrides.js
similarity index 100%
rename from Source/JavaScriptCore/API/tests/testapi-function-overrides.js
rename to Source/JavaScriptCore/API/tests/testapiScripts/testapi-function-overrides.js
diff --git a/Source/JavaScriptCore/API/tests/testapi.js b/Source/JavaScriptCore/API/tests/testapiScripts/testapi.js
similarity index 100%
rename from Source/JavaScriptCore/API/tests/testapi.js
rename to Source/JavaScriptCore/API/tests/testapiScripts/testapi.js
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index b281ca7..f845641 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,65 @@
+2019-07-12  Keith Miller  <keith_miller@apple.com>
+
+        Add API to get all the dependencies of a given JSScript
+        https://bugs.webkit.org/show_bug.cgi?id=199746
+
+        Reviewed by Saam Barati.
+
+        The method only returns the dependencies if the module was
+        actually evaluated. Technically, we know what the dependencies are
+        at the satisfy phase but for API simplicity we only provide that
+        information if the module graph was complete enough to at least
+        run.
+
+        This patch also fixes an issue where we would allow import
+        specifiers that didn't start "./" or "/". For reference, We have
+        this restriction to be consistent with the web/node. The
+        restriction exists in order to preserve namespace for
+        builtin-modules.
+
+        Lastly, this patch makes it so that we copy all scripts in the
+        API/tests/testapiScripts directory so they don't have to be
+        individually added to the xcode project.
+
+        * API/JSAPIGlobalObject.mm:
+        (JSC::computeValidImportSpecifier):
+        (JSC::JSAPIGlobalObject::moduleLoaderResolve):
+        (JSC::JSAPIGlobalObject::moduleLoaderImportModule):
+        * API/JSContext.mm:
+        (-[JSContext dependencyIdentifiersForModuleJSScript:]):
+        * API/JSContextPrivate.h:
+        * API/JSScript.h:
+        * API/tests/testapi.mm:
+        (testFetchWithTwoCycle):
+        (testFetchWithThreeCycle):
+        (testModuleBytecodeCache):
+        (+[JSContextFileLoaderDelegate newContext]):
+        (-[JSContextFileLoaderDelegate fetchModuleScript:]):
+        (-[JSContextFileLoaderDelegate findScriptForKey:]):
+        (-[JSContextFileLoaderDelegate context:fetchModuleForIdentifier:withResolveHandler:andRejectHandler:]):
+        (testDependenciesArray):
+        (testDependenciesEvaluationError):
+        (testDependenciesSyntaxError):
+        (testDependenciesBadImportId):
+        (testDependenciesMissingImport):
+        (testObjectiveCAPI):
+        * API/tests/testapiScripts/dependencyListTests/badModuleImportId.js: Added.
+        * API/tests/testapiScripts/dependencyListTests/bar.js: Added.
+        * API/tests/testapiScripts/dependencyListTests/dependenciesEntry.js: Added.
+        * API/tests/testapiScripts/dependencyListTests/foo.js: Added.
+        * API/tests/testapiScripts/dependencyListTests/missingImport.js: Added.
+        * API/tests/testapiScripts/dependencyListTests/referenceError.js: Added.
+        * API/tests/testapiScripts/dependencyListTests/syntaxError.js: Added.
+        * API/tests/testapiScripts/testapi-function-overrides.js: Renamed from Source/JavaScriptCore/API/tests/testapi-function-overrides.js.
+        * API/tests/testapiScripts/testapi.js: Renamed from Source/JavaScriptCore/API/tests/testapi.js.
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/ModuleLoader.js:
+        (dependencyKeysIfEvaluated):
+        * runtime/JSModuleLoader.cpp:
+        (JSC::JSModuleLoader::dependencyKeysIfEvaluated):
+        * runtime/JSModuleLoader.h:
+        * shell/CMakeLists.txt:
+
 2019-07-12  Justin Michaud  <justin_michaud@apple.com>
 
         B3 should reduce (integer) Sub(Neg(x), y) to Neg(Add(x, y))
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index ce97d98..87747bb 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -898,7 +898,6 @@
 		52CD0F5D2242F569004A18A5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
 		52CD0F5E2242F569004A18A5 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 932F5BD90822A1C700736975 /* JavaScriptCore.framework */; };
 		52CD0F682242F71C004A18A5 /* testdfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 52CD0F672242F71C004A18A5 /* testdfg.cpp */; };
-		52D13091221CE176009C836C /* foo.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 52D1308F221CE03A009C836C /* foo.js */; };
 		52F6C35E1E71EB080081F4CC /* WebAssemblyWrapperFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 52F6C35C1E71EB080081F4CC /* WebAssemblyWrapperFunction.h */; };
 		530A66B91FA3E78B0026A545 /* UnifiedSource3-mm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 530A66B11FA3E77A0026A545 /* UnifiedSource3-mm.mm */; };
 		530A66BA1FA3E78B0026A545 /* UnifiedSource4-mm.mm in Sources */ = {isa = PBXBuildFile; fileRef = 530A66B81FA3E77E0026A545 /* UnifiedSource4-mm.mm */; };
@@ -1082,7 +1081,6 @@
 		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 */; };
@@ -1111,7 +1109,6 @@
 		5C4196622270E0000047B7CD /* InspectorBackendDispatcherCompatibility.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C4196612270DFF30047B7CD /* InspectorBackendDispatcherCompatibility.cpp */; };
 		5C4E8E961DBEBE620036F1FC /* JSONParseTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C4E8E941DBEBDA20036F1FC /* JSONParseTest.cpp */; };
 		5D5D8AD10E0D0EBE00F9C692 /* libedit.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D5D8AD00E0D0EBE00F9C692 /* libedit.dylib */; };
-		5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 14D857740A4696C80032146C /* testapi.js */; };
 		5DBB1525131D0BD70056AD36 /* minidom.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = 1412110D0A48788700480255 /* minidom.js */; };
 		5DE6E5B30E1728EC00180407 /* create_hash_table in Headers */ = {isa = PBXBuildFile; fileRef = F692A8540255597D01FF60F7 /* create_hash_table */; settings = {ATTRIBUTES = (); }; };
 		623A37EC1B87A7C000754209 /* RegisterMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 623A37EB1B87A7BD00754209 /* RegisterMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -1287,10 +1284,10 @@
 		933040040E6A749400786E6A /* SmallStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = 93303FEA0E6A72C000786E6A /* SmallStrings.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		960097A60EBABB58007A7297 /* LabelScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 960097A50EBABB58007A7297 /* LabelScope.h */; };
 		9688CB150ED12B4E001D649F /* AssemblerBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB130ED12B4E001D649F /* AssemblerBuffer.h */; settings = {ATTRIBUTES = (Private, ); }; };
-		9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D649F /* X86Assembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		9688CB160ED12B4E001D6491 /* X86Registers.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D6491 /* X86Registers.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		9688CB160ED12B4E001D6492 /* X86_64Registers.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D6492 /* X86_64Registers.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		9688CB160ED12B4E001D6499 /* RegisterInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D6499 /* RegisterInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 9688CB140ED12B4E001D649F /* X86Assembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		969A07230ED1CE3300F1F681 /* BytecodeGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07210ED1CE3300F1F681 /* BytecodeGenerator.h */; };
 		969A072A0ED1CE6900F1F681 /* Label.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07270ED1CE6900F1F681 /* Label.h */; };
 		969A072B0ED1CE6900F1F681 /* RegisterID.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07280ED1CE6900F1F681 /* RegisterID.h */; };
@@ -1885,7 +1882,6 @@
 		FEB51F6C1A97B688001F921C /* Regress141809.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEB51F6B1A97B688001F921C /* Regress141809.mm */; };
 		FEB58C15187B8B160098EF0B /* ErrorHandlingScope.h in Headers */ = {isa = PBXBuildFile; fileRef = FEB58C13187B8B160098EF0B /* ErrorHandlingScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FECB8B271D25BB85006F2463 /* FunctionOverridesTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FECB8B251D25BB6E006F2463 /* FunctionOverridesTest.cpp */; };
-		FECB8B2A1D25CB5A006F2463 /* testapi-function-overrides.js in Copy Support Script */ = {isa = PBXBuildFile; fileRef = FECB8B291D25CABB006F2463 /* testapi-function-overrides.js */; };
 		FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = FED287B115EC9A5700DA8161 /* LLIntOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FEF040511AAE662D00BD28B0 /* CompareAndSwapTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */; };
@@ -2079,20 +2075,6 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXCopyFilesBuildPhase section */
-		5DBB1511131D0B130056AD36 /* Copy Support Script */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 12;
-			dstPath = testapiScripts;
-			dstSubfolderSpec = 16;
-			files = (
-				53C3D5E521ECE7720087FDFC /* basic.js in Copy Support Script */,
-				52D13091221CE176009C836C /* foo.js in Copy Support Script */,
-				FECB8B2A1D25CB5A006F2463 /* testapi-function-overrides.js in Copy Support Script */,
-				5DBB151B131D0B310056AD36 /* testapi.js in Copy Support Script */,
-			);
-			name = "Copy Support Script";
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 		5DBB1524131D0BA10056AD36 /* Copy Support Script */ = {
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
@@ -4082,10 +4064,10 @@
 		95C18D3E0C90E7EF00E72F73 /* JSRetainPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSRetainPtr.h; sourceTree = "<group>"; };
 		960097A50EBABB58007A7297 /* LabelScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelScope.h; sourceTree = "<group>"; };
 		9688CB130ED12B4E001D649F /* AssemblerBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AssemblerBuffer.h; sourceTree = "<group>"; };
-		9688CB140ED12B4E001D649F /* X86Assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X86Assembler.h; sourceTree = "<group>"; };
 		9688CB140ED12B4E001D6491 /* X86Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X86Registers.h; sourceTree = "<group>"; };
 		9688CB140ED12B4E001D6492 /* X86_64Registers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X86_64Registers.h; sourceTree = "<group>"; };
 		9688CB140ED12B4E001D6499 /* RegisterInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegisterInfo.h; sourceTree = "<group>"; };
+		9688CB140ED12B4E001D649F /* X86Assembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = X86Assembler.h; sourceTree = "<group>"; };
 		969A07200ED1CE3300F1F681 /* BytecodeGenerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BytecodeGenerator.cpp; sourceTree = "<group>"; };
 		969A07210ED1CE3300F1F681 /* BytecodeGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeGenerator.h; sourceTree = "<group>"; };
 		969A07270ED1CE6900F1F681 /* Label.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Label.h; sourceTree = "<group>"; };
@@ -7910,9 +7892,9 @@
 				FE10AAEA1F44D512009DEDC5 /* ProbeStack.h */,
 				9688CB140ED12B4E001D6499 /* RegisterInfo.h */,
 				FE533CA01F217C310016A1FE /* testmasm.cpp */,
+				9688CB140ED12B4E001D6492 /* X86_64Registers.h */,
 				9688CB140ED12B4E001D649F /* X86Assembler.h */,
 				9688CB140ED12B4E001D6491 /* X86Registers.h */,
-				9688CB140ED12B4E001D6492 /* X86_64Registers.h */,
 			);
 			path = assembler;
 			sourceTree = "<group>";
@@ -8656,7 +8638,7 @@
 				0F6B1CB91861244C00845D97 /* ArityCheckMode.h in Headers */,
 				A1A009C11831A26E00CF8711 /* ARM64Assembler.h in Headers */,
 				FE1E2C402240DD6200F6B729 /* ARM64EAssembler.h in Headers */,
-				A1A009C11831A26E00CF8722 /* ARM64Registers.h */,
+				A1A009C11831A26E00CF8722 /* ARM64Registers.h in Headers */,
 				86ADD1450FDDEA980006EEC2 /* ARMv7Assembler.h in Headers */,
 				86ADD1450FDDEA980006FFCC /* ARMv7Registers.h in Headers */,
 				0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */,
@@ -10098,9 +10080,9 @@
 				A7DCB97312E5193F00911940 /* WriteBarrier.h in Headers */,
 				C2B6D75318A33793004A9301 /* WriteBarrierInlines.h in Headers */,
 				0FC8150A14043BF500CFA603 /* WriteBarrierSupport.h in Headers */,
+				9688CB160ED12B4E001D6492 /* X86_64Registers.h in Headers */,
 				9688CB160ED12B4E001D649F /* X86Assembler.h in Headers */,
 				9688CB160ED12B4E001D6491 /* X86Registers.h in Headers */,
-				9688CB160ED12B4E001D6492 /* X86_64Registers.h in Headers */,
 				9959E92E1BD17FA4001AA413 /* xxd.pl in Headers */,
 				451539B912DC994500EF7AC4 /* Yarr.h in Headers */,
 				E3282BBB1FE930AF00EDAF71 /* YarrErrorCode.h in Headers */,
@@ -10208,7 +10190,7 @@
 			buildPhases = (
 				14BD59BC0A3E8F9000BAF59C /* Sources */,
 				14BD59BD0A3E8F9000BAF59C /* Frameworks */,
-				5DBB1511131D0B130056AD36 /* Copy Support Script */,
+				5366FDB222D5485B00BF94AF /* Copy Support Scripts */,
 				5DAFD6CD146B6B6E00FBEFB4 /* Install Support Script */,
 			);
 			buildRules = (
@@ -10539,6 +10521,24 @@
 			shellPath = /bin/sh;
 			shellScript = "Scripts/check-xcfilelists.sh\n";
 		};
+		5366FDB222D5485B00BF94AF /* Copy Support Scripts */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Support Scripts";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "rsync -r ${SRCROOT}/API/tests/testapiScripts ${BUILT_PRODUCTS_DIR}\n";
+		};
 		53B4BD091F68AF8900D2BEA3 /* Generate Unified Sources */ = {
 			isa = PBXShellScriptBuildPhase;
 			buildActionMask = 2147483647;
diff --git a/Source/JavaScriptCore/builtins/ModuleLoader.js b/Source/JavaScriptCore/builtins/ModuleLoader.js
index 1ebe2c0..c61543d 100644
--- a/Source/JavaScriptCore/builtins/ModuleLoader.js
+++ b/Source/JavaScriptCore/builtins/ModuleLoader.js
@@ -369,3 +369,20 @@
     this.linkAndEvaluateModule(entry.key, fetcher);
     return this.getModuleNamespaceObject(entry.module);
 }
+
+function dependencyKeysIfEvaluated(key)
+{
+    "use strict";
+
+    let entry = this.registry.@get(key);
+    if (!entry || !entry.evaluated)
+        return null;
+
+    let dependencies = entry.dependencies;
+    let length = dependencies.length;
+    let result = new @Array(length);
+    for (let i = 0; i < length; ++i)
+        result[i] = dependencies[i].key;
+
+    return result;
+}
diff --git a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp
index 4c68b9f..fad7c7a 100644
--- a/Source/JavaScriptCore/runtime/JSModuleLoader.cpp
+++ b/Source/JavaScriptCore/runtime/JSModuleLoader.cpp
@@ -86,6 +86,7 @@
     loadModule                     JSBuiltin                                  DontEnum|Function 3
     linkAndEvaluateModule          JSBuiltin                                  DontEnum|Function 2
     requestImportModule            JSBuiltin                                  DontEnum|Function 3
+    dependencyKeysIfEvaluated      JSBuiltin                                  DontEnum|Function 1
     getModuleNamespaceObject       moduleLoaderGetModuleNamespaceObject       DontEnum|Function 1
     parseModule                    moduleLoaderParseModule                    DontEnum|Function 2
     requestedModules               moduleLoaderRequestedModules               DontEnum|Function 1
@@ -125,6 +126,26 @@
     return vm.propertyNames->emptyIdentifier.impl();
 }
 
+JSArray* JSModuleLoader::dependencyKeysIfEvaluated(ExecState* exec, JSValue key)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* function = jsCast<JSObject*>(get(exec, vm.propertyNames->builtinNames().dependencyKeysIfEvaluatedPublicName()));
+    RETURN_IF_EXCEPTION(scope, nullptr);
+    CallData callData;
+    CallType callType = JSC::getCallData(vm, function, callData);
+    ASSERT(callType != CallType::None);
+
+    MarkedArgumentBuffer arguments;
+    arguments.append(key);
+
+    JSValue result = call(exec, function, callType, callData, this, arguments);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
+    return jsDynamicCast<JSArray*>(vm, result);
+}
+
 JSValue JSModuleLoader::provideFetch(ExecState* exec, JSValue key, const SourceCode& sourceCode)
 {
     VM& vm = exec->vm();
diff --git a/Source/JavaScriptCore/runtime/JSModuleLoader.h b/Source/JavaScriptCore/runtime/JSModuleLoader.h
index bda5143d..f541a8c 100644
--- a/Source/JavaScriptCore/runtime/JSModuleLoader.h
+++ b/Source/JavaScriptCore/runtime/JSModuleLoader.h
@@ -84,6 +84,7 @@
 
     // Utility functions.
     JSModuleNamespaceObject* getModuleNamespaceObject(ExecState*, JSValue moduleRecord);
+    JSArray* dependencyKeysIfEvaluated(ExecState*, JSValue key);
 
 protected:
     void finishCreation(ExecState*, VM&, JSGlobalObject*);
diff --git a/Source/JavaScriptCore/shell/CMakeLists.txt b/Source/JavaScriptCore/shell/CMakeLists.txt
index 9a49a33..f1cae5ff 100644
--- a/Source/JavaScriptCore/shell/CMakeLists.txt
+++ b/Source/JavaScriptCore/shell/CMakeLists.txt
@@ -92,12 +92,7 @@
     WEBKIT_EXECUTABLE(testdfg)
 
     file(COPY
-        "${JAVASCRIPTCORE_DIR}/API/tests/testapi.js"
-        DESTINATION
-        ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/testapiScripts
-    )
-    file(COPY
-        "${JAVASCRIPTCORE_DIR}/API/tests/testapi-function-overrides.js"
+        "${JAVASCRIPTCORE_DIR}/API/tests/testapiScripts"
         DESTINATION
         ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/testapiScripts
     )