summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp394
1 files changed, 394 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp b/Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp
new file mode 100644
index 000000000..37b8afc77
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2015 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 "ModuleLoaderObject.h"
+
+#include "BuiltinNames.h"
+#include "CodeProfiling.h"
+#include "Error.h"
+#include "Exception.h"
+#include "JSCInlines.h"
+#include "JSGlobalObjectFunctions.h"
+#include "JSInternalPromise.h"
+#include "JSInternalPromiseDeferred.h"
+#include "JSMap.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleRecord.h"
+#include "ModuleAnalyzer.h"
+#include "Nodes.h"
+#include "Parser.h"
+#include "ParserError.h"
+
+namespace JSC {
+
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectParseModule(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectRequestedModules(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectEvaluate(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectModuleDeclarationInstantiation(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectResolve(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectFetch(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectTranslate(ExecState*);
+static EncodedJSValue JSC_HOST_CALL moduleLoaderObjectInstantiate(ExecState*);
+
+}
+
+#include "ModuleLoaderObject.lut.h"
+
+namespace JSC {
+
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ModuleLoaderObject);
+
+const ClassInfo ModuleLoaderObject::s_info = { "ModuleLoader", &Base::s_info, &moduleLoaderObjectTable, CREATE_METHOD_TABLE(ModuleLoaderObject) };
+
+/* Source for ModuleLoaderObject.lut.h
+@begin moduleLoaderObjectTable
+ setStateToMax JSBuiltin DontEnum|Function 2
+ newRegistryEntry JSBuiltin DontEnum|Function 1
+ ensureRegistered JSBuiltin DontEnum|Function 1
+ forceFulfillPromise JSBuiltin DontEnum|Function 2
+ fulfillFetch JSBuiltin DontEnum|Function 2
+ fulfillTranslate JSBuiltin DontEnum|Function 2
+ fulfillInstantiate JSBuiltin DontEnum|Function 2
+ commitInstantiated JSBuiltin DontEnum|Function 3
+ instantiation JSBuiltin DontEnum|Function 3
+ requestFetch JSBuiltin DontEnum|Function 1
+ requestTranslate JSBuiltin DontEnum|Function 1
+ requestInstantiate JSBuiltin DontEnum|Function 1
+ requestResolveDependencies JSBuiltin DontEnum|Function 1
+ requestInstantiateAll JSBuiltin DontEnum|Function 1
+ requestLink JSBuiltin DontEnum|Function 1
+ requestReady JSBuiltin DontEnum|Function 1
+ link JSBuiltin DontEnum|Function 1
+ moduleDeclarationInstantiation moduleLoaderObjectModuleDeclarationInstantiation DontEnum|Function 2
+ moduleEvaluation JSBuiltin DontEnum|Function 2
+ evaluate moduleLoaderObjectEvaluate DontEnum|Function 2
+ provide JSBuiltin DontEnum|Function 3
+ loadAndEvaluateModule JSBuiltin DontEnum|Function 2
+ loadModule JSBuiltin DontEnum|Function 2
+ linkAndEvaluateModule JSBuiltin DontEnum|Function 1
+ parseModule moduleLoaderObjectParseModule DontEnum|Function 2
+ requestedModules moduleLoaderObjectRequestedModules DontEnum|Function 1
+ resolve moduleLoaderObjectResolve DontEnum|Function 1
+ fetch moduleLoaderObjectFetch DontEnum|Function 1
+ translate moduleLoaderObjectTranslate DontEnum|Function 2
+ instantiate moduleLoaderObjectInstantiate DontEnum|Function 2
+@end
+*/
+
+ModuleLoaderObject::ModuleLoaderObject(VM& vm, Structure* structure)
+ : JSNonFinalObject(vm, structure)
+{
+}
+
+void ModuleLoaderObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+ ASSERT(inherits(info()));
+
+ // Constant values for the state.
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "Fetch"), jsNumber(Status::Fetch));
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "Translate"), jsNumber(Status::Translate));
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "Instantiate"), jsNumber(Status::Instantiate));
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "ResolveDependencies"), jsNumber(Status::ResolveDependencies));
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "Link"), jsNumber(Status::Link));
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "Ready"), jsNumber(Status::Ready));
+
+ putDirectWithoutTransition(vm, Identifier::fromString(&vm, "registry"), JSMap::create(vm, globalObject->mapStructure()));
+}
+
+bool ModuleLoaderObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
+{
+ return getStaticFunctionSlot<Base>(exec, moduleLoaderObjectTable, jsCast<ModuleLoaderObject*>(object), propertyName, slot);
+}
+
+// ------------------------------ Functions --------------------------------
+
+static String printableModuleKey(ExecState* exec, JSValue key)
+{
+ if (key.isString() || key.isSymbol())
+ return key.toPropertyKey(exec).impl();
+ return exec->propertyNames().emptyIdentifier.impl();
+}
+
+JSValue ModuleLoaderObject::provide(ExecState* exec, JSValue key, Status status, const String& source)
+{
+ JSObject* function = jsCast<JSObject*>(get(exec, exec->propertyNames().builtinNames().providePublicName()));
+ CallData callData;
+ CallType callType = JSC::getCallData(function, callData);
+ ASSERT(callType != CallTypeNone);
+
+ MarkedArgumentBuffer arguments;
+ arguments.append(key);
+ arguments.append(jsNumber(status));
+ arguments.append(jsString(exec, source));
+
+ return call(exec, function, callType, callData, this, arguments);
+}
+
+JSInternalPromise* ModuleLoaderObject::loadAndEvaluateModule(ExecState* exec, JSValue moduleName, JSValue referrer)
+{
+ JSObject* function = jsCast<JSObject*>(get(exec, exec->propertyNames().builtinNames().loadAndEvaluateModulePublicName()));
+ CallData callData;
+ CallType callType = JSC::getCallData(function, callData);
+ ASSERT(callType != CallTypeNone);
+
+ MarkedArgumentBuffer arguments;
+ arguments.append(moduleName);
+ arguments.append(referrer);
+
+ return jsCast<JSInternalPromise*>(call(exec, function, callType, callData, this, arguments));
+}
+
+JSInternalPromise* ModuleLoaderObject::loadModule(ExecState* exec, JSValue moduleName, JSValue referrer)
+{
+ JSObject* function = jsCast<JSObject*>(get(exec, exec->propertyNames().builtinNames().loadModulePublicName()));
+ CallData callData;
+ CallType callType = JSC::getCallData(function, callData);
+ ASSERT(callType != CallTypeNone);
+
+ MarkedArgumentBuffer arguments;
+ arguments.append(moduleName);
+ arguments.append(referrer);
+
+ return jsCast<JSInternalPromise*>(call(exec, function, callType, callData, this, arguments));
+}
+
+JSInternalPromise* ModuleLoaderObject::linkAndEvaluateModule(ExecState* exec, JSValue moduleKey)
+{
+ JSObject* function = jsCast<JSObject*>(get(exec, exec->propertyNames().builtinNames().linkAndEvaluateModulePublicName()));
+ CallData callData;
+ CallType callType = JSC::getCallData(function, callData);
+ ASSERT(callType != CallTypeNone);
+
+ MarkedArgumentBuffer arguments;
+ arguments.append(moduleKey);
+
+ return jsCast<JSInternalPromise*>(call(exec, function, callType, callData, this, arguments));
+}
+
+JSInternalPromise* ModuleLoaderObject::resolve(ExecState* exec, JSValue name, JSValue referrer)
+{
+ if (Options::dumpModuleLoadingState())
+ dataLog("Loader [resolve] ", printableModuleKey(exec, name), "\n");
+
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ if (globalObject->globalObjectMethodTable()->moduleLoaderResolve)
+ return globalObject->globalObjectMethodTable()->moduleLoaderResolve(globalObject, exec, name, referrer);
+ JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
+ deferred->resolve(exec, name);
+ return deferred->promise();
+}
+
+JSInternalPromise* ModuleLoaderObject::fetch(ExecState* exec, JSValue key)
+{
+ if (Options::dumpModuleLoadingState())
+ dataLog("Loader [fetch] ", printableModuleKey(exec, key), "\n");
+
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ if (globalObject->globalObjectMethodTable()->moduleLoaderFetch)
+ return globalObject->globalObjectMethodTable()->moduleLoaderFetch(globalObject, exec, key);
+ JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
+ String moduleKey = key.toString(exec)->value(exec);
+ if (exec->hadException()) {
+ JSValue exception = exec->exception()->value();
+ exec->clearException();
+ deferred->reject(exec, exception);
+ return deferred->promise();
+ }
+ deferred->reject(exec, createError(exec, makeString("Could not open the module '", moduleKey, "'.")));
+ return deferred->promise();
+}
+
+JSInternalPromise* ModuleLoaderObject::translate(ExecState* exec, JSValue key, JSValue payload)
+{
+ if (Options::dumpModuleLoadingState())
+ dataLog("Loader [translate] ", printableModuleKey(exec, key), "\n");
+
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ if (globalObject->globalObjectMethodTable()->moduleLoaderTranslate)
+ return globalObject->globalObjectMethodTable()->moduleLoaderTranslate(globalObject, exec, key, payload);
+ JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
+ deferred->resolve(exec, payload);
+ return deferred->promise();
+}
+
+JSInternalPromise* ModuleLoaderObject::instantiate(ExecState* exec, JSValue key, JSValue source)
+{
+ if (Options::dumpModuleLoadingState())
+ dataLog("Loader [instantiate] ", printableModuleKey(exec, key), "\n");
+
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ if (globalObject->globalObjectMethodTable()->moduleLoaderInstantiate)
+ return globalObject->globalObjectMethodTable()->moduleLoaderInstantiate(globalObject, exec, key, source);
+ JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
+ deferred->resolve(exec, jsUndefined());
+ return deferred->promise();
+}
+
+JSValue ModuleLoaderObject::evaluate(ExecState* exec, JSValue key, JSValue moduleRecordValue)
+{
+ if (Options::dumpModuleLoadingState())
+ dataLog("Loader [evaluate] ", printableModuleKey(exec, key), "\n");
+
+ JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+ if (globalObject->globalObjectMethodTable()->moduleLoaderEvaluate)
+ return globalObject->globalObjectMethodTable()->moduleLoaderEvaluate(globalObject, exec, key, moduleRecordValue);
+
+ JSModuleRecord* moduleRecord = jsDynamicCast<JSModuleRecord*>(moduleRecordValue);
+ if (!moduleRecord)
+ return jsUndefined();
+ return moduleRecord->evaluate(exec);
+}
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectParseModule(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ const Identifier moduleKey = exec->argument(0).toPropertyKey(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ String source = exec->argument(1).toString(exec)->value(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ SourceCode sourceCode = makeSource(source, moduleKey.impl());
+
+ CodeProfiling profile(sourceCode);
+
+ ParserError error;
+ std::unique_ptr<ModuleProgramNode> moduleProgramNode = parse<ModuleProgramNode>(
+ &vm, sourceCode, Identifier(), JSParserBuiltinMode::NotBuiltin,
+ JSParserStrictMode::Strict, SourceParseMode::ModuleAnalyzeMode, SuperBinding::NotNeeded, error);
+
+ if (error.isValid()) {
+ throwVMError(exec, error.toErrorObject(exec->lexicalGlobalObject(), sourceCode));
+ return JSValue::encode(jsUndefined());
+ }
+ ASSERT(moduleProgramNode);
+
+ ModuleAnalyzer moduleAnalyzer(exec, moduleKey, sourceCode, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables());
+ JSModuleRecord* moduleRecord = moduleAnalyzer.analyze(*moduleProgramNode);
+
+ return JSValue::encode(moduleRecord);
+}
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectRequestedModules(ExecState* exec)
+{
+ JSModuleRecord* moduleRecord = jsDynamicCast<JSModuleRecord*>(exec->argument(0));
+ if (!moduleRecord)
+ return JSValue::encode(constructEmptyArray(exec, nullptr));
+
+ JSArray* result = constructEmptyArray(exec, nullptr, moduleRecord->requestedModules().size());
+ size_t i = 0;
+ for (auto& key : moduleRecord->requestedModules())
+ result->putDirectIndex(exec, i++, jsString(exec, key.get()));
+
+ return JSValue::encode(result);
+}
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectModuleDeclarationInstantiation(ExecState* exec)
+{
+ JSModuleRecord* moduleRecord = jsDynamicCast<JSModuleRecord*>(exec->argument(0));
+ if (!moduleRecord)
+ return JSValue::encode(jsUndefined());
+
+ if (Options::dumpModuleLoadingState())
+ dataLog("Loader [link] ", moduleRecord->moduleKey(), "\n");
+
+ moduleRecord->link(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ return JSValue::encode(jsUndefined());
+}
+
+// ------------------------------ Hook Functions ---------------------------
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectResolve(ExecState* exec)
+{
+ // Hook point, Loader.resolve.
+ // https://whatwg.github.io/loader/#browser-resolve
+ // Take the name and resolve it to the unique identifier for the resource location.
+ // For example, take the "jquery" and return the URL for the resource.
+ ModuleLoaderObject* loader = jsDynamicCast<ModuleLoaderObject*>(exec->thisValue());
+ if (!loader)
+ return JSValue::encode(jsUndefined());
+ return JSValue::encode(loader->resolve(exec, exec->argument(0), exec->argument(1)));
+}
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectFetch(ExecState* exec)
+{
+ // Hook point, Loader.fetch
+ // https://whatwg.github.io/loader/#browser-fetch
+ // Take the key and fetch the resource actually.
+ // For example, JavaScriptCore shell can provide the hook fetching the resource
+ // from the local file system.
+ ModuleLoaderObject* loader = jsDynamicCast<ModuleLoaderObject*>(exec->thisValue());
+ if (!loader)
+ return JSValue::encode(jsUndefined());
+ return JSValue::encode(loader->fetch(exec, exec->argument(0)));
+}
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectTranslate(ExecState* exec)
+{
+ // Hook point, Loader.translate
+ // https://whatwg.github.io/loader/#browser-translate
+ // Take the key and the fetched source code and translate it to the ES6 source code.
+ // Typically it is used by the transpilers.
+ ModuleLoaderObject* loader = jsDynamicCast<ModuleLoaderObject*>(exec->thisValue());
+ if (!loader)
+ return JSValue::encode(jsUndefined());
+ return JSValue::encode(loader->translate(exec, exec->argument(0), exec->argument(1)));
+}
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectInstantiate(ExecState* exec)
+{
+ // Hook point, Loader.instantiate
+ // https://whatwg.github.io/loader/#browser-instantiate
+ // Take the key and the translated source code, and instantiate the module record
+ // by parsing the module source code.
+ // It has the chance to provide the optional module instance that is different from
+ // the ordinary one.
+ ModuleLoaderObject* loader = jsDynamicCast<ModuleLoaderObject*>(exec->thisValue());
+ if (!loader)
+ return JSValue::encode(jsUndefined());
+ return JSValue::encode(loader->instantiate(exec, exec->argument(0), exec->argument(1)));
+}
+
+// ------------------- Additional Hook Functions ---------------------------
+
+EncodedJSValue JSC_HOST_CALL moduleLoaderObjectEvaluate(ExecState* exec)
+{
+ // To instrument and retrieve the errors raised from the module execution,
+ // we inserted the hook point here.
+
+ ModuleLoaderObject* loader = jsDynamicCast<ModuleLoaderObject*>(exec->thisValue());
+ if (!loader)
+ return JSValue::encode(jsUndefined());
+ return JSValue::encode(loader->evaluate(exec, exec->argument(0), exec->argument(1)));
+}
+
+} // namespace JSC