Skip to content
This repository was archived by the owner on Jun 24, 2022. It is now read-only.

Commit 16bf741

Browse files
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): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@239933 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 72a9385 commit 16bf741

29 files changed

+1044
-39
lines changed

Source/JavaScriptCore/API/APICast.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,21 +128,26 @@ inline JSC::VM* toJS(JSContextGroupRef g)
128128
return reinterpret_cast<JSC::VM*>(const_cast<OpaqueJSContextGroup*>(g));
129129
}
130130

131-
inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v)
131+
inline JSValueRef toRef(JSC::VM& vm, JSC::JSValue v)
132132
{
133-
ASSERT(exec->vm().currentThreadIsHoldingAPILock());
133+
ASSERT(vm.currentThreadIsHoldingAPILock());
134134
#if !CPU(ADDRESS64)
135135
if (!v)
136136
return 0;
137137
if (!v.isCell())
138-
return reinterpret_cast<JSValueRef>(JSC::jsAPIValueWrapper(exec, v).asCell());
138+
return reinterpret_cast<JSValueRef>(JSC::JSAPIValueWrapper::create(vm, v));
139139
return reinterpret_cast<JSValueRef>(v.asCell());
140140
#else
141-
UNUSED_PARAM(exec);
141+
UNUSED_PARAM(vm);
142142
return bitwise_cast<JSValueRef>(v);
143143
#endif
144144
}
145145

146+
inline JSValueRef toRef(JSC::ExecState* exec, JSC::JSValue v)
147+
{
148+
return toRef(exec->vm(), v);
149+
}
150+
146151
inline JSObjectRef toRef(JSC::JSObject* o)
147152
{
148153
return reinterpret_cast<JSObjectRef>(o);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (C) 2019 Apple Inc. All rights reserved.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
*
13+
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#include "config.h"
27+
#include "JSAPIGlobalObject.h"
28+
29+
#if !JSC_OBJC_API_ENABLED
30+
31+
namespace JSC {
32+
33+
const ClassInfo JSAPIGlobalObject::s_info = { "GlobalObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSAPIGlobalObject) };
34+
35+
const GlobalObjectMethodTable JSAPIGlobalObject::s_globalObjectMethodTable = {
36+
&supportsRichSourceInfo,
37+
&shouldInterruptScript,
38+
&javaScriptRuntimeFlags,
39+
nullptr, // queueTaskToEventLoop
40+
&shouldInterruptScriptBeforeTimeout,
41+
nullptr, // moduleLoaderImportModule
42+
nullptr, // moduleLoaderResolve
43+
nullptr, // moduleLoaderFetch
44+
nullptr, // moduleLoaderCreateImportMetaProperties
45+
nullptr, // moduleLoaderEvaluate
46+
nullptr, // promiseRejectionTracker
47+
nullptr, // defaultLanguage
48+
nullptr, // compileStreaming
49+
nullptr, // instantiateStreaming
50+
};
51+
52+
}
53+
54+
#endif
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright (C) 2019 Apple Inc. All rights reserved.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
*
13+
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#pragma once
27+
28+
#include "JSGlobalObject.h"
29+
30+
namespace JSC {
31+
32+
class JSAPIGlobalObject : public JSGlobalObject {
33+
public:
34+
using Base = JSGlobalObject;
35+
36+
DECLARE_EXPORT_INFO;
37+
static const GlobalObjectMethodTable s_globalObjectMethodTable;
38+
39+
static JSAPIGlobalObject* create(VM& vm, Structure* structure)
40+
{
41+
auto* object = new (NotNull, allocateCell<JSAPIGlobalObject>(vm.heap)) JSAPIGlobalObject(vm, structure);
42+
object->finishCreation(vm);
43+
return object;
44+
}
45+
46+
static Structure* createStructure(VM& vm, JSValue prototype)
47+
{
48+
auto* result = Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
49+
result->setTransitionWatchpointIsLikelyToBeFired(true);
50+
return result;
51+
}
52+
53+
static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString* moduleNameValue, JSValue parameters, const SourceOrigin&);
54+
static Identifier moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue);
55+
static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
56+
static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue);
57+
58+
private:
59+
JSAPIGlobalObject(VM& vm, Structure* structure)
60+
: Base(vm, structure, &s_globalObjectMethodTable)
61+
{ }
62+
};
63+
64+
}
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Copyright (C) 2019 Apple Inc. All rights reserved.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
*
13+
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24+
*/
25+
26+
#import "config.h"
27+
#import "JSAPIGlobalObject.h"
28+
29+
#if JSC_OBJC_API_ENABLED
30+
31+
#import "APICast.h"
32+
#import "CatchScope.h"
33+
#import "Completion.h"
34+
#import "Error.h"
35+
#import "Exception.h"
36+
#import "JSContextInternal.h"
37+
#import "JSInternalPromiseDeferred.h"
38+
#import "JSNativeStdFunction.h"
39+
#import "JSScriptInternal.h"
40+
#import "JSSourceCode.h"
41+
#import "JSValueInternal.h"
42+
#import "JSVirtualMachineInternal.h"
43+
#import "JavaScriptCore.h"
44+
#import "ObjectConstructor.h"
45+
#import "SourceOrigin.h"
46+
47+
#import <wtf/URL.h>
48+
49+
namespace JSC {
50+
51+
const ClassInfo JSAPIGlobalObject::s_info = { "GlobalObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSAPIGlobalObject) };
52+
53+
const GlobalObjectMethodTable JSAPIGlobalObject::s_globalObjectMethodTable = {
54+
&supportsRichSourceInfo,
55+
&shouldInterruptScript,
56+
&javaScriptRuntimeFlags,
57+
nullptr, // queueTaskToEventLoop
58+
&shouldInterruptScriptBeforeTimeout,
59+
&moduleLoaderImportModule, // moduleLoaderImportModule
60+
&moduleLoaderResolve, // moduleLoaderResolve
61+
&moduleLoaderFetch, // moduleLoaderFetch
62+
&moduleLoaderCreateImportMetaProperties, // moduleLoaderCreateImportMetaProperties
63+
nullptr, // moduleLoaderEvaluate
64+
nullptr, // promiseRejectionTracker
65+
nullptr, // defaultLanguage
66+
nullptr, // compileStreaming
67+
nullptr, // instantiateStreaming
68+
};
69+
70+
Identifier JSAPIGlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue referrer, JSValue)
71+
{
72+
VM& vm = exec->vm();
73+
auto scope = DECLARE_THROW_SCOPE(vm);
74+
ASSERT_UNUSED(globalObject, globalObject == exec->lexicalGlobalObject());
75+
ASSERT(key.isString() || key.isSymbol());
76+
String name = key.toWTFString(exec);
77+
78+
URL referrerURL(URL(), jsCast<JSString*>(referrer)->tryGetValue());
79+
RELEASE_ASSERT(referrerURL.isValid());
80+
81+
URL url = URL(referrerURL, name);
82+
if (url.isValid())
83+
return Identifier::fromString(exec, url);
84+
85+
throwVMError(exec, scope, "Could not form valid URL from identifier and base"_s);
86+
return { };
87+
}
88+
89+
JSInternalPromise* JSAPIGlobalObject::moduleLoaderImportModule(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSString* specifierValue, JSValue, const SourceOrigin& sourceOrigin)
90+
{
91+
VM& vm = globalObject->vm();
92+
auto scope = DECLARE_CATCH_SCOPE(vm);
93+
auto reject = [&] (JSValue exception) -> JSInternalPromise* {
94+
scope.clearException();
95+
auto* promise = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
96+
scope.clearException();
97+
return promise->reject(exec, exception);
98+
};
99+
100+
auto import = [&] (URL& url) {
101+
auto result = importModule(exec, Identifier::fromString(&vm, url), jsUndefined(), jsUndefined());
102+
if (UNLIKELY(scope.exception()))
103+
return reject(scope.exception());
104+
return result;
105+
};
106+
107+
auto specifier = specifierValue->value(exec);
108+
if (UNLIKELY(scope.exception())) {
109+
JSValue exception = scope.exception();
110+
scope.clearException();
111+
return reject(exception);
112+
}
113+
114+
URL absoluteURL(URL(), specifier);
115+
if (absoluteURL.isValid())
116+
return import(absoluteURL);
117+
118+
if (!specifier.startsWith('/') && !specifier.startsWith("./") && !specifier.startsWith("../"))
119+
return reject(createError(exec, "Module specifier does not start with \"/\", \"./\", or \"../\"."_s));
120+
121+
if (specifier.startsWith('/')) {
122+
absoluteURL = URL(URL({ }, "file://"), specifier);
123+
if (absoluteURL.isValid())
124+
return import(absoluteURL);
125+
}
126+
127+
auto noBaseErrorMessage = "Could not determine the base URL for loading."_s;
128+
if (sourceOrigin.isNull())
129+
return reject(createError(exec, noBaseErrorMessage));
130+
131+
auto referrer = sourceOrigin.string();
132+
URL baseURL(URL(), referrer);
133+
if (!baseURL.isValid())
134+
return reject(createError(exec, noBaseErrorMessage));
135+
136+
URL url(baseURL, specifier);
137+
if (!url.isValid())
138+
return reject(createError(exec, "could not determine a valid URL for module specifier"_s));
139+
140+
return import(url);
141+
}
142+
143+
JSInternalPromise* JSAPIGlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue, JSValue)
144+
{
145+
VM& vm = globalObject->vm();
146+
auto scope = DECLARE_CATCH_SCOPE(vm);
147+
148+
ASSERT(globalObject == exec->lexicalGlobalObject());
149+
JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject->globalExec())];
150+
151+
JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
152+
RETURN_IF_EXCEPTION(scope, nullptr);
153+
154+
Identifier moduleKey = key.toPropertyKey(exec);
155+
if (UNLIKELY(scope.exception())) {
156+
JSValue exception = scope.exception();
157+
scope.clearException();
158+
return deferred->reject(exec, exception);
159+
}
160+
161+
if (UNLIKELY(![context moduleLoaderDelegate]))
162+
return deferred->reject(exec, createError(exec, "No module loader provided."));
163+
164+
auto deferredPromise = Strong<JSInternalPromiseDeferred>(vm, deferred);
165+
auto strongKey = Strong<JSString>(vm, jsSecureCast<JSString*>(vm, key));
166+
auto* resolve = JSNativeStdFunction::create(vm, globalObject, 1, "resolve", [=] (ExecState* exec) {
167+
VM& vm = exec->vm();
168+
// This captures the globalObject but that's ok because our structure keeps it alive anyway.
169+
JSContext *context = [JSContext contextWithJSGlobalContextRef:toGlobalRef(globalObject->globalExec())];
170+
id script = valueToObject(context, toRef(exec, exec->argument(0)));
171+
172+
MarkedArgumentBuffer args;
173+
if (UNLIKELY(![script isKindOfClass:[JSScript class]])) {
174+
args.append(createTypeError(exec, "First argument of resolution callback is not a JSScript"));
175+
call(exec, deferredPromise->JSPromiseDeferred::reject(), args, "This should never be seen...");
176+
return encodedJSUndefined();
177+
}
178+
179+
const String& source = getJSScriptSourceCode(static_cast<JSScript *>(script));
180+
args.append(JSSourceCode::create(vm, makeSource(source, SourceOrigin(moduleKey.string()), URL({ }, moduleKey.string()), TextPosition(), JSC::SourceProviderSourceType::Module)));
181+
call(exec, deferredPromise->JSPromiseDeferred::resolve(), args, "This should never be seen...");
182+
return encodedJSUndefined();
183+
});
184+
185+
auto* reject = JSNativeStdFunction::create(vm, globalObject, 1, "reject", [=] (ExecState* exec) {
186+
MarkedArgumentBuffer args;
187+
args.append(exec->argument(0));
188+
189+
call(exec, deferredPromise->JSPromiseDeferred::reject(), args, "This should never be seen...");
190+
return encodedJSUndefined();
191+
});
192+
193+
[[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]];
194+
if (context.exception) {
195+
deferred->reject(exec, toJS(exec, [context.exception JSValueRef]));
196+
context.exception = nil;
197+
}
198+
return deferred->promise();
199+
}
200+
201+
JSObject* JSAPIGlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSModuleRecord*, JSValue)
202+
{
203+
VM& vm = exec->vm();
204+
auto scope = DECLARE_THROW_SCOPE(vm);
205+
206+
JSObject* metaProperties = constructEmptyObject(exec, globalObject->nullPrototypeObjectStructure());
207+
RETURN_IF_EXCEPTION(scope, nullptr);
208+
209+
metaProperties->putDirect(vm, Identifier::fromString(&vm, "filename"), key);
210+
RETURN_IF_EXCEPTION(scope, nullptr);
211+
212+
return metaProperties;
213+
}
214+
215+
}
216+
217+
#endif // JSC_OBJC_API_ENABLED

0 commit comments

Comments
 (0)