Title: [239774] trunk/Source/_javascript_Core
Revision
239774
Author
keith_mil...@apple.com
Date
2019-01-09 09:40:00 -0800 (Wed, 09 Jan 2019)

Log Message

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):

Modified Paths

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();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to