Skip to content

Commit

Permalink
[ES6] Return JSInternalPromise as result of evaluateModule
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=148173

Reviewed by Saam Barati.

Now evaluateModule returns JSInternalPromise* as its result value.
When an error occurs while loading or executing the modules,
this promise is rejected by that error. By leveraging this, we implemented
asynchronous error reporting when executing the modules in JSC shell.

And this patch also changes the evaluateModule signature to accept the entry
point by the moduleName. By using it, JSC shell can start executing the modules
with the entry point module name.

* builtins/ModuleLoaderObject.js:
(loadModule):
* jsc.cpp:
(dumpException):
(runWithScripts):
* runtime/Completion.cpp:
(JSC::evaluateModule):
* runtime/Completion.h:
* runtime/JSInternalPromise.cpp:
(JSC::JSInternalPromise::then):
* runtime/JSInternalPromise.h:
* runtime/ModuleLoaderObject.cpp:
(JSC::ModuleLoaderObject::requestInstantiateAll):
(JSC::ModuleLoaderObject::loadModule):
(JSC::ModuleLoaderObject::resolve):
(JSC::ModuleLoaderObject::fetch):
(JSC::ModuleLoaderObject::translate):
(JSC::ModuleLoaderObject::instantiate):
(JSC::moduleLoaderObjectParseModule):
* runtime/ModuleLoaderObject.h:

Canonical link: https://commits.webkit.org/166521@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188894 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
Constellation committed Aug 24, 2015
1 parent 36ffdd0 commit 4e4c32b
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 46 deletions.
37 changes: 37 additions & 0 deletions Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,40 @@
2015-08-24 Yusuke Suzuki <utatane.tea@gmail.com>

[ES6] Return JSInternalPromise as result of evaluateModule
https://bugs.webkit.org/show_bug.cgi?id=148173

Reviewed by Saam Barati.

Now evaluateModule returns JSInternalPromise* as its result value.
When an error occurs while loading or executing the modules,
this promise is rejected by that error. By leveraging this, we implemented
asynchronous error reporting when executing the modules in JSC shell.

And this patch also changes the evaluateModule signature to accept the entry
point by the moduleName. By using it, JSC shell can start executing the modules
with the entry point module name.

* builtins/ModuleLoaderObject.js:
(loadModule):
* jsc.cpp:
(dumpException):
(runWithScripts):
* runtime/Completion.cpp:
(JSC::evaluateModule):
* runtime/Completion.h:
* runtime/JSInternalPromise.cpp:
(JSC::JSInternalPromise::then):
* runtime/JSInternalPromise.h:
* runtime/ModuleLoaderObject.cpp:
(JSC::ModuleLoaderObject::requestInstantiateAll):
(JSC::ModuleLoaderObject::loadModule):
(JSC::ModuleLoaderObject::resolve):
(JSC::ModuleLoaderObject::fetch):
(JSC::ModuleLoaderObject::translate):
(JSC::ModuleLoaderObject::instantiate):
(JSC::moduleLoaderObjectParseModule):
* runtime/ModuleLoaderObject.h:

2015-08-24 Basile Clement <basile_clement@apple.com>

REPTACH is not a word
Expand Down
18 changes: 18 additions & 0 deletions Source/JavaScriptCore/builtins/ModuleLoaderObject.js
Expand Up @@ -396,6 +396,24 @@ function requestInstantiateAll(key)
return this.requestResolveDependencies(key);
}

function loadModule(moduleName, referrer)
{
"use strict";

var loader = this;
// Loader.resolve hook point.
// resolve: moduleName => Promise(moduleKey)
// 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.
return this.resolve(moduleName, referrer).then(function (key) {
// FIXME: Now, we don't implement the linking phase yet.
// So here, we just call requestInstantiateAll to only perform the module loading.
// At last, it should be replaced with requestReady.
// https://bugs.webkit.org/show_bug.cgi?id=148172
return loader.requestInstantiateAll(key);
});
}

function provide(key, stage, value)
{
"use strict";
Expand Down
51 changes: 37 additions & 14 deletions Source/JavaScriptCore/jsc.cpp
Expand Up @@ -1336,15 +1336,19 @@ int main(int argc, char** argv)
jscExit(res);
}

static void dumpException(GlobalObject* globalObject, JSValue exception)
{
printf("Exception: %s\n", exception.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
Identifier stackID = Identifier::fromString(globalObject->globalExec(), "stack");
JSValue stackValue = exception.get(globalObject->globalExec(), stackID);
if (!stackValue.isUndefinedOrNull())
printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
}

static void dumpException(GlobalObject* globalObject, NakedPtr<Exception> evaluationException)
{
if (evaluationException) {
printf("Exception: %s\n", evaluationException->value().toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
Identifier stackID = Identifier::fromString(globalObject->globalExec(), "stack");
JSValue stackValue = evaluationException->value().get(globalObject->globalExec(), stackID);
if (!stackValue.isUndefinedOrNull())
printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
}
if (evaluationException)
dumpException(globalObject, evaluationException->value());
}

static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump, bool module)
Expand All @@ -1357,18 +1361,37 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scr
JSC::Options::dumpGeneratedBytecodes() = true;

VM& vm = globalObject->vm();
bool success = true;

#if ENABLE(ES6_MODULES)
JSFunction* errorHandler = JSFunction::create(vm, globalObject, 1, String(), [&](ExecState* exec) {
success = false;
dumpException(globalObject, exec->argument(0));
return JSValue::encode(jsUndefined());
});
#endif

#if ENABLE(SAMPLING_FLAGS)
SamplingFlags::start();
#endif

bool success = true;
for (size_t i = 0; i < scripts.size(); i++) {
#if ENABLE(ES6_MODULES)
JSInternalPromise* promise = nullptr;
#endif
if (scripts[i].isFile) {
fileName = scripts[i].argument;
if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
return false; // fail early so we can catch missing files
script = scriptBuffer.data();
if (module) {
#if ENABLE(ES6_MODULES)
promise = evaluateModule(globalObject->globalExec(), fileName);
#else
RELEASE_ASSERT_NOT_REACHED();
#endif
} else {
if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
return false; // fail early so we can catch missing files
script = scriptBuffer.data();
}
} else {
script = scripts[i].argument;
fileName = ASCIILiteral("[Command Line]");
Expand All @@ -1378,10 +1401,10 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scr

if (module) {
#if ENABLE(ES6_MODULES)
NakedPtr<Exception> evaluationException;
evaluateModule(globalObject->globalExec(), jscSource(script, fileName), evaluationException);
dumpException(globalObject, evaluationException);
if (!promise)
promise = evaluateModule(globalObject->globalExec(), jscSource(script, fileName));
globalObject->globalExec()->clearException();
promise->then(globalObject->globalExec(), nullptr, errorHandler);
globalObject->vm().drainMicrotasks();
#else
RELEASE_ASSERT_NOT_REACHED();
Expand Down
62 changes: 42 additions & 20 deletions Source/JavaScriptCore/runtime/Completion.cpp
Expand Up @@ -30,6 +30,8 @@
#include "Interpreter.h"
#include "JSCInlines.h"
#include "JSGlobalObject.h"
#include "JSInternalPromise.h"
#include "JSInternalPromiseDeferred.h"
#include "JSLock.h"
#include "JSModuleRecord.h"
#include "ModuleAnalyzer.h"
Expand Down Expand Up @@ -111,43 +113,63 @@ JSValue evaluate(ExecState* exec, const SourceCode& source, JSValue thisValue, N
return result;
}

void evaluateModule(ExecState* exec, const SourceCode& source, NakedPtr<Exception>& returnedException)
static JSInternalPromise* evaluateModule(const JSLockHolder&, ExecState* exec, JSGlobalObject* globalObject, JSValue moduleName, JSValue referrer)
{
return globalObject->moduleLoader()->loadModule(exec, moduleName, referrer);
}

static JSInternalPromise* evaluateModule(const JSLockHolder& lock, ExecState* exec, JSGlobalObject* globalObject, const Identifier& moduleName)
{
JSValue moduleNameValue;
if (moduleName.isSymbol())
moduleNameValue = Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*moduleName.impl()));
else
moduleNameValue = jsString(&exec->vm(), moduleName.impl());

return evaluateModule(lock, exec, globalObject, moduleNameValue, jsUndefined());
}

JSInternalPromise* evaluateModule(ExecState* exec, const SourceCode& source)
{
JSLockHolder lock(exec);
RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable());
RELEASE_ASSERT(!exec->vm().isCollectorBusy());

CodeProfiling profile(source);

JSGlobalObject* globalObject = exec->vmEntryGlobalObject();

// Generate the unique key for the source-provided module.
PrivateName privateName(PrivateName::Description, "EntryPointModule");
Symbol* key = Symbol::create(exec->vm(), *privateName.uid());

ModuleLoaderObject* moduleLoader = globalObject->moduleLoader();

// Insert the given source code to the ModuleLoader registry as the fetched registry entry.
moduleLoader->provide(exec, key, ModuleLoaderObject::Status::Fetch, source.toString());
globalObject->moduleLoader()->provide(exec, key, ModuleLoaderObject::Status::Fetch, source.toString());
if (exec->hadException()) {
returnedException = exec->exception();
JSValue exception = exec->exception()->value();
exec->clearException();
return;
JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::create(exec, globalObject);
deferred->reject(exec, exception);
return deferred->promise();
}

// FIXME: Now, we don't implement the linking phase yet.
// So here, we just call requestInstantiateAll to only perform the module loading.
// At last, it should be replaced with requestReady.
// https://bugs.webkit.org/show_bug.cgi?id=148172
moduleLoader->requestInstantiateAll(exec, key);
return evaluateModule(lock, exec, globalObject, key, jsUndefined());
}

// FIXME: We should also handle the asynchronous Syntax Errors that will be delivered by the rejected promise.
// https://bugs.webkit.org/show_bug.cgi?id=148173
if (exec->hadException()) {
returnedException = exec->exception();
exec->clearException();
return;
}
JSInternalPromise* evaluateModule(ExecState* exec, const Identifier& moduleName)
{
JSLockHolder lock(exec);
RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable());
RELEASE_ASSERT(!exec->vm().isCollectorBusy());

return evaluateModule(lock, exec, exec->vmEntryGlobalObject(), moduleName);
}

JSInternalPromise* evaluateModule(ExecState* exec, const String& moduleName)
{
JSLockHolder lock(exec);
RELEASE_ASSERT(exec->vm().atomicStringTable() == wtfThreadData().atomicStringTable());
RELEASE_ASSERT(!exec->vm().isCollectorBusy());

return evaluateModule(lock, exec, exec->vmEntryGlobalObject(), Identifier::fromString(exec, moduleName));
}

} // namespace JSC
5 changes: 4 additions & 1 deletion Source/JavaScriptCore/runtime/Completion.h
Expand Up @@ -34,6 +34,7 @@ class JSScope;
class ParserError;
class SourceCode;
class VM;
class JSInternalPromise;

JS_EXPORT_PRIVATE bool checkSyntax(VM&, const SourceCode&, ParserError&);
JS_EXPORT_PRIVATE bool checkSyntax(ExecState*, const SourceCode&, JSValue* exception = 0);
Expand All @@ -44,7 +45,9 @@ inline JSValue evaluate(ExecState* exec, const SourceCode& sourceCode, JSValue t
NakedPtr<Exception> unused;
return evaluate(exec, sourceCode, thisValue, unused);
}
JS_EXPORT_PRIVATE void evaluateModule(ExecState*, const SourceCode&, NakedPtr<Exception>& returnedException);
JS_EXPORT_PRIVATE JSInternalPromise* evaluateModule(ExecState*, const SourceCode&);
JS_EXPORT_PRIVATE JSInternalPromise* evaluateModule(ExecState*, const Identifier& moduleName);
JS_EXPORT_PRIVATE JSInternalPromise* evaluateModule(ExecState*, const String& moduleName);

} // namespace JSC

Expand Down
15 changes: 15 additions & 0 deletions Source/JavaScriptCore/runtime/JSInternalPromise.cpp
Expand Up @@ -26,6 +26,7 @@
#include "config.h"
#include "JSInternalPromise.h"

#include "BuiltinNames.h"
#include "JSCJSValueInlines.h"
#include "JSCellInlines.h"
#include "StructureInlines.h"
Expand All @@ -51,4 +52,18 @@ JSInternalPromise::JSInternalPromise(VM& vm, Structure* structure)
{
}

JSInternalPromise* JSInternalPromise::then(ExecState* exec, JSFunction* onFulfilled, JSFunction* onRejected)
{
JSObject* function = jsCast<JSObject*>(get(exec, exec->propertyNames().builtinNames().thenPublicName()));
CallData callData;
CallType callType = JSC::getCallData(function, callData);
ASSERT(callType != CallTypeNone);

MarkedArgumentBuffer arguments;
arguments.append(onFulfilled ? onFulfilled : jsUndefined());
arguments.append(onRejected ? onRejected : jsUndefined());

return jsCast<JSInternalPromise*>(call(exec, function, callType, callData, this, arguments));
}

} // namespace JSC
12 changes: 12 additions & 0 deletions Source/JavaScriptCore/runtime/JSInternalPromise.h
Expand Up @@ -30,6 +30,16 @@

namespace JSC {

class JSFunction;

// JSInternalPromise is completely separated instance from the JSPromise.
// Since its prototype and constructor are different from the exposed Promises' ones,
// all the user modification onto the exposed Promise does not have effect on JSInternalPromise.
//
// e.g.
// Replacing Promise.prototype.then with the user-customized one does not effect on JSInternalPromise.
//
// CAUTION: Must not leak the JSInternalPromise to the user space to keep its integrity.
class JSInternalPromise : public JSPromise {
public:
typedef JSPromise Base;
Expand All @@ -39,6 +49,8 @@ class JSInternalPromise : public JSPromise {

DECLARE_EXPORT_INFO;

JS_EXPORT_PRIVATE JSInternalPromise* then(ExecState*, JSFunction* = nullptr, JSFunction* = nullptr);

private:
JSInternalPromise(VM&, Structure*);
};
Expand Down

0 comments on commit 4e4c32b

Please sign in to comment.