Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (239773 => 239774)
--- trunk/Source/_javascript_Core/ChangeLog 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-01-09 17:40:00 UTC (rev 239774)
@@ -1,3 +1,33 @@
+2019-01-08 Keith Miller <keith_mil...@apple.com>
+
+ builtins should be able to use async/await
+ https://bugs.webkit.org/show_bug.cgi?id=193263
+
+ Reviewed by Saam Barati.
+
+ This patch makes it possible to use async functions when writing builtin JS code.
+
+ * Scripts/wkbuiltins/builtins_generator.py:
+ (BuiltinsGenerator.generate_embedded_code_string_section_for_function):
+ * Scripts/wkbuiltins/builtins_model.py:
+ (BuiltinFunction.__init__):
+ (BuiltinFunction.fromString):
+ (BuiltinFunction.__str__):
+ * builtins/BuiltinExecutables.cpp:
+ (JSC::BuiltinExecutables::createExecutable):
+ * builtins/ModuleLoader.js:
+ (requestInstantiate):
+ (async.loadModule):
+ (async.loadAndEvaluateModule):
+ (async.requestImportModule):
+ (loadModule): Deleted.
+ (): Deleted.
+ (loadAndEvaluateModule): Deleted.
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator):
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::parseInner):
+
2019-01-08 Yusuke Suzuki <yusukesuz...@slowstart.org>
Array.prototype.flat/flatMap have a minor bug in ArraySpeciesCreate
Modified: trunk/Source/_javascript_Core/Scripts/wkbuiltins/builtins_generator.py (239773 => 239774)
--- trunk/Source/_javascript_Core/Scripts/wkbuiltins/builtins_generator.py 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/Scripts/wkbuiltins/builtins_generator.py 2019-01-09 17:40:00 UTC (rev 239774)
@@ -114,7 +114,11 @@
def generate_embedded_code_string_section_for_function(self, function):
text = function.function_source
# Wrap it in parens to avoid adding to global scope.
- text = "(function " + text[text.index("("):] + ")"
+ function_type_string = "function "
+ if function.is_async:
+ function_type_string = "async " + function_type_string
+
+ text = "(" + function_type_string + text[text.index("("):] + ")"
embeddedSourceLength = len(text) + 1 # For extra \n.
# Lazy way to escape quotes, I think?
textLines = json.dumps(text)[1:-1].split("\\n")
Modified: trunk/Source/_javascript_Core/Scripts/wkbuiltins/builtins_model.py (239773 => 239774)
--- trunk/Source/_javascript_Core/Scripts/wkbuiltins/builtins_model.py 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/Scripts/wkbuiltins/builtins_model.py 2019-01-09 17:40:00 UTC (rev 239774)
@@ -42,14 +42,15 @@
},
}
-functionHeadRegExp = re.compile(r"(?:@[\w|=\[\] \"\.]+\s*\n)*function\s+\w+\s*\(.*?\)", re.MULTILINE | re.DOTALL)
+functionHeadRegExp = re.compile(r"(?:@[\w|=\[\] \"\.]+\s*\n)*(?:async\s+)?function\s+\w+\s*\(.*?\)", re.MULTILINE | re.DOTALL)
functionGlobalPrivateRegExp = re.compile(r".*^@globalPrivate", re.MULTILINE | re.DOTALL)
functionIntrinsicRegExp = re.compile(r".*^@intrinsic=(\w+)", re.MULTILINE | re.DOTALL)
functionIsConstructorRegExp = re.compile(r".*^@constructor", re.MULTILINE | re.DOTALL)
functionIsGetterRegExp = re.compile(r".*^@getter", re.MULTILINE | re.DOTALL)
+functionIsAsyncRegExp = re.compile(r"(async)?\s*function", re.MULTILINE | re.DOTALL)
functionNameRegExp = re.compile(r"function\s+(\w+)\s*\(", re.MULTILINE | re.DOTALL)
functionOverriddenNameRegExp = re.compile(r".*^@overriddenName=(\".+\")$", re.MULTILINE | re.DOTALL)
-functionParameterFinder = re.compile(r"^function\s+(?:\w+)\s*\(((?:\s*\w+)?\s*(?:\s*,\s*\w+)*)?\s*\)", re.MULTILINE | re.DOTALL)
+functionParameterFinder = re.compile(r"^(?:async\s+)?function\s+(?:\w+)\s*\(((?:\s*\w+)?\s*(?:\s*,\s*\w+)*)?\s*\)", re.MULTILINE | re.DOTALL)
multilineCommentRegExp = re.compile(r"\/\*.*?\*\/", re.MULTILINE | re.DOTALL)
singleLineCommentRegExp = re.compile(r"\/\/.*?\n", re.MULTILINE | re.DOTALL)
@@ -100,10 +101,11 @@
class BuiltinFunction:
- def __init__(self, function_name, function_source, parameters, is_constructor, is_global_private, intrinsic, overridden_name):
+ def __init__(self, function_name, function_source, parameters, is_async, is_constructor, is_global_private, intrinsic, overridden_name):
self.function_name = function_name
self.function_source = function_source
self.parameters = parameters
+ self.is_async = is_async
self.is_constructor = is_constructor
self.is_global_private = is_global_private
self.intrinsic = intrinsic
@@ -133,6 +135,8 @@
function_source = multipleEmptyLinesRegExp.sub("\n", function_source)
function_name = functionNameRegExp.findall(function_source)[0]
+ async_match = functionIsAsyncRegExp.match(function_source)
+ is_async = async_match != None and async_match.group(1) == "async"
is_constructor = functionIsConstructorRegExp.match(function_source) != None
is_getter = functionIsGetterRegExp.match(function_source) != None
is_global_private = functionGlobalPrivateRegExp.match(function_source) != None
@@ -146,7 +150,7 @@
if not overridden_name:
overridden_name = "static_cast<const char*>(nullptr)"
- return BuiltinFunction(function_name, function_source, parameters, is_constructor, is_global_private, intrinsic, overridden_name)
+ return BuiltinFunction(function_name, function_source, parameters, is_async, is_constructor, is_global_private, intrinsic, overridden_name)
def __str__(self):
interface = "%s(%s)" % (self.function_name, ', '.join(self.parameters))
@@ -153,6 +157,9 @@
if self.is_constructor:
interface = interface + " [Constructor]"
+ if self.is_async:
+ interface = "async " + interface
+
return interface
def __lt__(self, other):
Modified: trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp (239773 => 239774)
--- trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp 2019-01-09 17:40:00 UTC (rev 239774)
@@ -85,6 +85,8 @@
UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility)
{
+ // FIXME: Can we just make MetaData computation be constexpr and have the compiler do this for us?
+ // https://bugs.webkit.org/show_bug.cgi?id=193272
// Someone should get mad at me for writing this code. But, it prevents us from recursing into
// the parser, and hence, from throwing stack overflow when parsing a builtin.
StringView view = source.view();
@@ -91,41 +93,35 @@
RELEASE_ASSERT(!view.isNull());
RELEASE_ASSERT(view.is8Bit());
auto* characters = view.characters8();
- RELEASE_ASSERT(view.length() >= 15); // strlen("(function (){})") == 15
- RELEASE_ASSERT(characters[0] == '(');
- RELEASE_ASSERT(characters[1] == 'f');
- RELEASE_ASSERT(characters[2] == 'u');
- RELEASE_ASSERT(characters[3] == 'n');
- RELEASE_ASSERT(characters[4] == 'c');
- RELEASE_ASSERT(characters[5] == 't');
- RELEASE_ASSERT(characters[6] == 'i');
- RELEASE_ASSERT(characters[7] == 'o');
- RELEASE_ASSERT(characters[8] == 'n');
- RELEASE_ASSERT(characters[9] == ' ');
- RELEASE_ASSERT(characters[10] == '(');
+ const char* regularFunctionBegin = "(function (";
+ const char* asyncFunctionBegin = "(async function (";
+ RELEASE_ASSERT(view.length() >= strlen("(function (){})"));
+ bool isAsyncFunction = view.length() >= strlen("(async function (){})") && !memcmp(characters, asyncFunctionBegin, strlen(asyncFunctionBegin));
+ RELEASE_ASSERT(isAsyncFunction || !memcmp(characters, regularFunctionBegin, strlen(regularFunctionBegin)));
+ unsigned asyncOffset = isAsyncFunction ? strlen("async ") : 0;
+ unsigned parametersStart = strlen("function (") + asyncOffset;
JSTokenLocation start;
start.line = -1;
start.lineStartOffset = std::numeric_limits<unsigned>::max();
- start.startOffset = 10;
+ start.startOffset = parametersStart;
start.endOffset = std::numeric_limits<unsigned>::max();
JSTokenLocation end;
end.line = 1;
end.lineStartOffset = 0;
- end.startOffset = 1;
+ end.startOffset = strlen("(") + asyncOffset;
end.endOffset = std::numeric_limits<unsigned>::max();
- unsigned startColumn = 10; // strlen("function (") == 10
- int functionKeywordStart = 1; // (f
- int functionNameStart = 10;
- int parametersStart = 10;
+ unsigned startColumn = parametersStart;
+ int functionKeywordStart = strlen("(") + asyncOffset;
+ int functionNameStart = parametersStart;
bool isInStrictContext = false;
bool isArrowFunctionBodyExpression = false;
unsigned parameterCount;
{
- unsigned i = 11;
+ unsigned i = parametersStart + 1;
unsigned commas = 0;
bool sawOneParam = false;
bool hasRestParam = false;
@@ -199,14 +195,15 @@
positionBeforeLastNewline.offset = offsetOfLastNewline;
positionBeforeLastNewline.lineStartOffset = positionBeforeLastNewlineLineStartOffset;
- SourceCode newSource = source.subExpression(10, view.length() - closeBraceOffsetFromEnd, 0, 10);
+ SourceCode newSource = source.subExpression(parametersStart, view.length() - closeBraceOffsetFromEnd, 0, parametersStart);
bool isBuiltinDefaultClassConstructor = constructorKind != ConstructorKind::None;
UnlinkedFunctionKind kind = isBuiltinDefaultClassConstructor ? UnlinkedNormalFunction : UnlinkedBuiltinFunction;
+ SourceParseMode parseMode = isAsyncFunction ? SourceParseMode::AsyncFunctionMode : SourceParseMode::NormalFunctionMode;
FunctionMetadataNode metadata(
start, end, startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart,
isInStrictContext, constructorKind, constructorKind == ConstructorKind::Extends ? SuperBinding::Needed : SuperBinding::NotNeeded,
- parameterCount, SourceParseMode::NormalFunctionMode, isArrowFunctionBodyExpression);
+ parameterCount, parseMode, isArrowFunctionBodyExpression);
metadata.finishParsing(newSource, Identifier(), FunctionMode::FunctionExpression);
metadata.overrideName(name);
@@ -242,6 +239,14 @@
metadataFromParser->setEndPosition(positionBeforeLastNewlineFromParser);
if (metadata != *metadataFromParser || positionBeforeLastNewlineFromParser != positionBeforeLastNewline) {
+ dataLogLn("Expected Metadata:\n", metadata);
+ dataLogLn("Metadata from parser:\n", *metadataFromParser);
+ dataLogLn("positionBeforeLastNewlineFromParser.line ", positionBeforeLastNewlineFromParser.line);
+ dataLogLn("positionBeforeLastNewlineFromParser.offset ", positionBeforeLastNewlineFromParser.offset);
+ dataLogLn("positionBeforeLastNewlineFromParser.lineStartOffset ", positionBeforeLastNewlineFromParser.lineStartOffset);
+ dataLogLn("positionBeforeLastNewline.line ", positionBeforeLastNewline.line);
+ dataLogLn("positionBeforeLastNewline.offset ", positionBeforeLastNewline.offset);
+ dataLogLn("positionBeforeLastNewline.lineStartOffset ", positionBeforeLastNewline.lineStartOffset);
WTFLogAlways("Metadata of parser and hand rolled parser don't match\n");
CRASH();
}
Modified: trunk/Source/_javascript_Core/builtins/ModuleLoader.js (239773 => 239774)
--- trunk/Source/_javascript_Core/builtins/ModuleLoader.js 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/builtins/ModuleLoader.js 2019-01-09 17:40:00 UTC (rev 239774)
@@ -187,32 +187,32 @@
if (entry.instantiate)
return entry.instantiate;
- var instantiatePromise = this.requestFetch(entry, parameters, fetcher).then((source) => {
+ var instantiatePromise = (async () => {
+ var source = await this.requestFetch(entry, parameters, fetcher);
// https://html.spec.whatwg.org/#fetch-a-single-module-script
// Now fetching request succeeds. Then even if instantiation fails, we should cache it.
// Instantiation won't be retried.
if (entry.instantiate)
- return entry.instantiate;
+ return await entry.instantiate;
entry.instantiate = instantiatePromise;
var key = entry.key;
- return this.parseModule(key, source).then((moduleRecord) => {
- var dependenciesMap = moduleRecord.dependenciesMap;
- var requestedModules = this.requestedModules(moduleRecord);
- var dependencies = @newArrayWithSize(requestedModules.length);
- for (var i = 0, length = requestedModules.length; i < length; ++i) {
- var depName = requestedModules[i];
- var depKey = this.resolveSync(depName, key, fetcher);
- var depEntry = this.ensureRegistered(depKey);
- @putByValDirect(dependencies, i, depEntry);
- dependenciesMap.@set(depName, depEntry);
- }
- entry.dependencies = dependencies;
- entry.module = moduleRecord;
- @setStateToMax(entry, @ModuleSatisfy);
- return entry;
- });
- });
+ var moduleRecord = await this.parseModule(key, source);
+ var dependenciesMap = moduleRecord.dependenciesMap;
+ var requestedModules = this.requestedModules(moduleRecord);
+ var dependencies = @newArrayWithSize(requestedModules.length);
+ for (var i = 0, length = requestedModules.length; i < length; ++i) {
+ var depName = requestedModules[i];
+ var depKey = this.resolveSync(depName, key, fetcher);
+ var depEntry = this.ensureRegistered(depKey);
+ @putByValDirect(dependencies, i, depEntry);
+ dependenciesMap.@set(depName, depEntry);
+ }
+ entry.dependencies = dependencies;
+ entry.module = moduleRecord;
+ @setStateToMax(entry, @ModuleSatisfy);
+ return entry;
+ })();
return instantiatePromise;
}
@@ -328,7 +328,7 @@
this.fulfillFetch(entry, value);
}
-function loadModule(moduleName, parameters, fetcher)
+async function loadModule(moduleName, parameters, fetcher)
{
"use strict";
@@ -336,11 +336,9 @@
// 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, @undefined, fetcher).then((key) => {
- return this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher, new @Set);
- }).then((entry) => {
- return entry.key;
- });
+ let key = await this.resolve(moduleName, @undefined, fetcher);
+ let entry = await this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher, new @Set);
+ return entry.key;
}
function linkAndEvaluateModule(key, fetcher)
@@ -355,21 +353,19 @@
return this.moduleEvaluation(entry, fetcher);
}
-function loadAndEvaluateModule(moduleName, parameters, fetcher)
+async function loadAndEvaluateModule(moduleName, parameters, fetcher)
{
"use strict";
- return this.loadModule(moduleName, parameters, fetcher).then((key) => {
- return this.linkAndEvaluateModule(key, fetcher);
- });
+ let key = await this.loadModule(moduleName, parameters, fetcher);
+ return await this.linkAndEvaluateModule(key, fetcher);
}
-function requestImportModule(key, parameters, fetcher)
+async function requestImportModule(key, parameters, fetcher)
{
"use strict";
- return this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher, new @Set).then((entry) => {
- this.linkAndEvaluateModule(entry.key, fetcher);
- return this.getModuleNamespaceObject(entry.module);
- });
+ let entry = await this.requestSatisfy(this.ensureRegistered(key), parameters, fetcher, new @Set);
+ this.linkAndEvaluateModule(entry.key, fetcher);
+ return this.getModuleNamespaceObject(entry.module);
}
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (239773 => 239774)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2019-01-09 17:40:00 UTC (rev 239774)
@@ -692,7 +692,8 @@
CallArguments args(*this, nullptr, 1);
emitLoad(args.thisRegister(), jsUndefined());
- auto varPromiseConstructor = variable(propertyNames().builtinNames().PromisePrivateName());
+ auto& builtinNames = propertyNames().builtinNames();
+ auto varPromiseConstructor = variable(m_isBuiltinFunction ? builtinNames.InternalPromisePrivateName() : builtinNames.PromisePrivateName());
move(scope.get(), emitResolveScope(scope.get(), varPromiseConstructor));
emitGetFromScope(args.argumentRegister(0), scope.get(), varPromiseConstructor, ThrowIfNotFound);
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (239773 => 239774)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2019-01-09 16:08:05 UTC (rev 239773)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2019-01-09 17:40:00 UTC (rev 239774)
@@ -279,7 +279,8 @@
VariableEnvironment& lexicalVariables = scope->lexicalVariables();
const HashSet<UniquedStringImpl*>& closedVariableCandidates = scope->closedVariableCandidates();
for (UniquedStringImpl* candidate : closedVariableCandidates) {
- if (!lexicalVariables.contains(candidate) && !varDeclarations.contains(candidate) && !candidate->isSymbol()) {
+ // FIXME: We allow async to leak because it appearing as a closed variable is a side effect of trying to parse async arrow functions.
+ if (!lexicalVariables.contains(candidate) && !varDeclarations.contains(candidate) && !candidate->isSymbol() && candidate != m_vm->propertyNames->async.impl()) {
dataLog("Bad global capture in builtin: '", candidate, "'\n");
dataLog(m_source->view());
CRASH();