Skip to content

Commit 77cb9e4

Browse files
author
Ian Halliday
committed
Add %AsyncFunction% builtin APIs to runtime
Add %AsyncFunction% constructor and %AsyncFunctionPrototype% object to the runtime. Change async function instances' prototypes to point to %AsyncFunctionPrototype%. Like %GeneratorFunction% there is no property exposing %AsyncFunction% on the global object. It is only accessible through the %AsyncFunctionPrototype% which is only accessible from async function instances. Also renamed the contrarily named hasPrototype parameter on JavascriptLibrary::ScriptFunctionTypeHandler() to noPrototypeProperty.
1 parent c18573e commit 77cb9e4

13 files changed

Lines changed: 102 additions & 25 deletions

lib/Parser/parse.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ CatchPidRefList *Parser::EnsureCatchPidRefList()
240240
return this->m_catchPidRefList;
241241
}
242242

243-
HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isGenerator, CompileScriptException *pse, void (Parser::*validateFunction)())
243+
HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isGenerator, bool isAsync, CompileScriptException *pse, void (Parser::*validateFunction)())
244244
{
245245
AssertPsz(pszSrc);
246246
AssertMemN(pse);
@@ -274,6 +274,7 @@ HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isG
274274
// Give the scanner the source and get the first token
275275
m_pscan->SetText(pszSrc, 0, encodedCharCount, 0, grfscr);
276276
m_pscan->SetYieldIsKeyword(isGenerator);
277+
m_pscan->SetAwaitIsKeyword(isAsync);
277278
m_pscan->Scan();
278279

279280
uint nestedCount = 0;
@@ -305,6 +306,7 @@ HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isG
305306
pnodeFnc->sxFnc.pnodeRest = nullptr;
306307
pnodeFnc->sxFnc.deferredStub = nullptr;
307308
pnodeFnc->sxFnc.SetIsGenerator(isGenerator);
309+
pnodeFnc->sxFnc.SetIsAsync(isAsync);
308310
m_ppnodeVar = &pnodeFnc->sxFnc.pnodeVars;
309311
m_currentNodeFunc = pnodeFnc;
310312
m_currentNodeDeferredFunc = NULL;

lib/Parser/parse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class Parser
158158

159159
void RestorePidRefForSym(Symbol *sym);
160160

161-
HRESULT ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isGenerator, CompileScriptException *pse, void (Parser::*validateFunction)());
161+
HRESULT ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isGenerator, bool isAsync, CompileScriptException *pse, void (Parser::*validateFunction)());
162162

163163
// Should be called when the UTF-8 source was produced from UTF-16. This is really CESU-8 source in that it encodes surragate pairs
164164
// as 2 three byte sequences instead of 4 bytes as required UTF-8. It also is is loss-less converison of invalid UTF-16 sequences.

lib/Runtime/Library/GlobalObject.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ namespace Js
731731
return pEvalFunction;
732732
}
733733

734-
void GlobalObject::ValidateSyntax(ScriptContext* scriptContext, const wchar_t *source, int sourceLength, bool isGenerator, void (Parser::*validateSyntax)())
734+
void GlobalObject::ValidateSyntax(ScriptContext* scriptContext, const wchar_t *source, int sourceLength, bool isGenerator, bool isAsync, void (Parser::*validateSyntax)())
735735
{
736736
Assert(sourceLength >= 0);
737737

@@ -752,7 +752,7 @@ namespace Js
752752
utf8Source = reinterpret_cast< LPUTF8 >( tempAlloc.Realloc(utf8Source, cbUtf8Buffer, cbSource + 1) );
753753

754754
Parser parser(scriptContext);
755-
hrParser = parser.ValidateSyntax(utf8Source, cbSource, isGenerator, &se, validateSyntax);
755+
hrParser = parser.ValidateSyntax(utf8Source, cbSource, isGenerator, isAsync, &se, validateSyntax);
756756
}
757757
END_TRANSLATE_EXCEPTION_TO_HRESULT(hr);
758758
END_LEAVE_SCRIPT_INTERNAL(scriptContext);

lib/Runtime/Library/GlobalObject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ namespace Js
8787
static Var EntryRejitFunction(RecyclableObject *function, CallInfo callInfo, ...);
8888
#endif /* IR_VIEWER */
8989

90-
static void ValidateSyntax(ScriptContext* scriptContext, const wchar_t *source, int sourceLength, bool isGenerator, void (Parser::*validateSyntax)());
90+
static void ValidateSyntax(ScriptContext* scriptContext, const wchar_t *source, int sourceLength, bool isGenerator, bool isAsync, void (Parser::*validateSyntax)());
9191
static void UpdateThisForEval(Var &varThis, ModuleID moduleID, ScriptContext *scriptContext, BOOL strictMode) ;
9292
static ScriptFunction* DefaultEvalHelper(ScriptContext* scriptContext, const wchar_t *source, int sourceLength, ModuleID moduleID, ulong grfscr, LPCOLESTR pszTitle, BOOL registerDocument, BOOL isIndirect, BOOL strictMode);
9393
static ScriptFunction* ProfileModeEvalHelper(ScriptContext* scriptContext, const wchar_t *source, int sourceLength, ModuleID moduleID, ulong grfscr, LPCOLESTR pszTitle, BOOL registerDocument, BOOL isIndirect, BOOL strictMode);

lib/Runtime/Library/JavascriptBuiltInFunctionList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ BUILTIN(JavascriptFunction, Bind, EntryBind, FunctionInfo::ErrorOnNew)
137137
BUILTIN(JavascriptFunction, Call, EntryCall, FunctionInfo::ErrorOnNew)
138138
BUILTIN(JavascriptFunction, ToString, EntryToString, FunctionInfo::ErrorOnNew | FunctionInfo::HasNoSideEffect)
139139
BUILTIN(JavascriptFunction, SymbolHasInstance, EntrySymbolHasInstance, FunctionInfo::ErrorOnNew)
140+
BUILTIN(JavascriptFunction, NewAsyncFunctionInstance, NewAsyncFunctionInstance, FunctionInfo::SkipDefaultNewObject)
140141
BUILTIN(JavascriptNumber, IsNaN, EntryIsNaN, FunctionInfo::ErrorOnNew)
141142
BUILTIN(JavascriptNumber, IsFinite, EntryIsFinite, FunctionInfo::ErrorOnNew)
142143
BUILTIN(JavascriptNumber, IsInteger, EntryIsInteger, FunctionInfo::ErrorOnNew)

lib/Runtime/Library/JavascriptFunction.cpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,10 @@ namespace Js
138138

139139
static wchar_t const funcName[] = L"function anonymous";
140140
static wchar_t const genFuncName[] = L"function* anonymous";
141+
static wchar_t const asyncFuncName[] = L"async function anonymous";
141142
static wchar_t const bracket[] = L" {\012";
142143

143-
Var JavascriptFunction::NewInstanceHelper(ScriptContext *scriptContext, RecyclableObject* function, CallInfo callInfo, Js::ArgumentReader& args, bool isGenerator /* = false */)
144+
Var JavascriptFunction::NewInstanceHelper(ScriptContext *scriptContext, RecyclableObject* function, CallInfo callInfo, Js::ArgumentReader& args, FunctionKind functionKind /* = FunctionKind::Normal */)
144145
{
145146
JavascriptLibrary* library = function->GetLibrary();
146147

@@ -177,7 +178,9 @@ namespace Js
177178
// Create a string representing the anonymous function
178179
Assert(CountNewlines(funcName) + CountNewlines(bracket) == numberLinesPrependedToAnonymousFunction); // Be sure to add exactly one line to anonymous function
179180

180-
JavascriptString *bs = isGenerator ?
181+
JavascriptString *bs = functionKind == FunctionKind::Async ?
182+
library->CreateStringFromCppLiteral(asyncFuncName) :
183+
functionKind == FunctionKind::Generator ?
181184
library->CreateStringFromCppLiteral(genFuncName) :
182185
library->CreateStringFromCppLiteral(funcName);
183186
bs = JavascriptString::Concat(bs, formals);
@@ -201,14 +204,18 @@ namespace Js
201204
EvalMapString key(sourceString, sourceLen, moduleID, strictMode, /* isLibraryCode = */ false);
202205
if (!scriptContext->IsInNewFunctionMap(key, &pfuncBodyCache))
203206
{
204-
// ES3 and ES5 specs require validation of the formal list and the function body
205-
206207
// Validate formals here
207-
scriptContext->GetGlobalObject()->ValidateSyntax(scriptContext, formals->GetSz(), formals->GetLength(), isGenerator, &Parser::ValidateFormals);
208+
scriptContext->GetGlobalObject()->ValidateSyntax(
209+
scriptContext, formals->GetSz(), formals->GetLength(),
210+
functionKind == FunctionKind::Generator, functionKind == FunctionKind::Async,
211+
&Parser::ValidateFormals);
208212
if (fnBody != NULL)
209213
{
210214
// Validate function body
211-
scriptContext->GetGlobalObject()->ValidateSyntax(scriptContext, fnBody->GetSz(), fnBody->GetLength(), isGenerator, &Parser::ValidateSourceElementList);
215+
scriptContext->GetGlobalObject()->ValidateSyntax(
216+
scriptContext, fnBody->GetSz(), fnBody->GetLength(),
217+
functionKind == FunctionKind::Generator, functionKind == FunctionKind::Async,
218+
&Parser::ValidateSourceElementList);
212219
}
213220

214221
pfuncScript = scriptContext->GetGlobalObject()->EvalHelper(scriptContext, sourceString, sourceLen, moduleID, fscrNil, Constants::FunctionCode, TRUE, TRUE, strictMode);
@@ -238,7 +245,7 @@ namespace Js
238245

239246
JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_FUNCTION(pfuncScript, EtwTrace::GetFunctionId(pfuncScript->GetFunctionProxy())));
240247

241-
if (isGenerator)
248+
if (functionKind == FunctionKind::Generator)
242249
{
243250
Assert(pfuncScript->GetFunctionInfo()->IsGenerator());
244251
auto pfuncVirt = static_cast<GeneratorVirtualScriptFunction*>(pfuncScript);
@@ -275,6 +282,16 @@ namespace Js
275282
return NewInstanceHelper(scriptContext, function, callInfo, args);
276283
}
277284

285+
Var JavascriptFunction::NewAsyncFunctionInstance(RecyclableObject* function, CallInfo callInfo, ...)
286+
{
287+
// Get called when creating a new async function through the constructor (e.g. af.__proto__.constructor)
288+
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
289+
290+
ARGUMENTS(args, callInfo);
291+
292+
return JavascriptFunction::NewInstanceHelper(function->GetScriptContext(), function, callInfo, args, JavascriptFunction::FunctionKind::Async);
293+
}
294+
278295
//
279296
// Dummy EntryPoint for Function.prototype
280297
//

lib/Runtime/Library/JavascriptFunction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@ namespace Js
4545
static const charcount_t DIAG_MAX_FUNCTION_STRING = 256;
4646

4747
protected:
48-
static Var NewInstanceHelper(ScriptContext *scriptContext, RecyclableObject* function, CallInfo callInfo, Js::ArgumentReader& args, bool isGenerator = false);
48+
enum class FunctionKind { Normal, Generator, Async };
49+
static Var NewInstanceHelper(ScriptContext *scriptContext, RecyclableObject* function, CallInfo callInfo, Js::ArgumentReader& args, FunctionKind functionKind = FunctionKind::Normal);
4950

5051
JavascriptFunction(DynamicType * type);
52+
5153
public:
5254
JavascriptFunction(DynamicType * type, FunctionInfo * functionInfo);
5355
JavascriptFunction(DynamicType * type, FunctionInfo * functionInfo, ConstructorCache* cache);
@@ -62,6 +64,8 @@ namespace Js
6264
static FunctionInfo Call;
6365
static FunctionInfo ToString;
6466
static FunctionInfo SymbolHasInstance;
67+
68+
static FunctionInfo NewAsyncFunctionInstance;
6569
};
6670

6771
static const int numberLinesPrependedToAnonymousFunction = 1;
@@ -77,6 +81,8 @@ namespace Js
7781
static Var EntryToString(RecyclableObject* function, CallInfo callInfo, ...);
7882
static Var EntrySymbolHasInstance(RecyclableObject* function, CallInfo callInfo, ...);
7983

84+
static Var NewAsyncFunctionInstance(RecyclableObject* function, CallInfo callInfo, ...);
85+
8086
static bool Is(Var aValue);
8187
static JavascriptFunction* FromVar(Var aValue);
8288
Var CallFunction(Arguments args);

lib/Runtime/Library/JavascriptGeneratorFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ namespace Js
9696

9797
ARGUMENTS(args, callInfo);
9898

99-
return JavascriptFunction::NewInstanceHelper(function->GetScriptContext(), function, callInfo, args, /* isGenerator: */ true);
99+
return JavascriptFunction::NewInstanceHelper(function->GetScriptContext(), function, callInfo, args, FunctionKind::Generator);
100100
}
101101

102102
JavascriptString* JavascriptGeneratorFunction::GetDisplayNameImpl() const

lib/Runtime/Library/JavascriptGeneratorFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ namespace Js
7575
{
7676
private:
7777
friend class JavascriptGeneratorFunction;
78-
friend Var Js::JavascriptFunction::NewInstanceHelper(ScriptContext*, RecyclableObject*, CallInfo, ArgumentReader&, bool);
78+
friend Var Js::JavascriptFunction::NewInstanceHelper(ScriptContext*, RecyclableObject*, CallInfo, ArgumentReader&, Js::JavascriptFunction::FunctionKind);
7979

8080
JavascriptGeneratorFunction* realFunction;
8181

lib/Runtime/Library/JavascriptLibrary.cpp

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ namespace Js
430430
generatorFunctionPrototype = nullptr;
431431
generatorPrototype = nullptr;
432432
iteratorPrototype = nullptr;
433+
asyncFunctionPrototype = nullptr;
433434

434435
if (scriptContext->GetConfig()->IsES6SymbolEnabled())
435436
{
@@ -520,6 +521,13 @@ namespace Js
520521
DynamicType::New(scriptContext, TypeIds_Object, proto, nullptr,
521522
DeferredTypeHandler<InitializeGeneratorPrototype>::GetDefaultInstance()));
522523
}
524+
525+
if (scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
526+
{
527+
asyncFunctionPrototype = DynamicObject::New(recycler,
528+
DynamicType::New(scriptContext, TypeIds_Object, functionPrototype, nullptr,
529+
DeferredTypeHandler<InitializeAsyncFunctionPrototype>::GetDefaultInstance()));
530+
}
523531
}
524532

525533
void JavascriptLibrary::InitializeTypes()
@@ -975,19 +983,21 @@ namespace Js
975983
return functionTypeHandler;
976984
}
977985

978-
DynamicTypeHandler * JavascriptLibrary::ScriptFunctionTypeHandler(bool hasPrototype, bool isAnonymousFunction)
986+
DynamicTypeHandler * JavascriptLibrary::ScriptFunctionTypeHandler(bool noPrototypeProperty, bool isAnonymousFunction)
979987
{
980988
DynamicTypeHandler * scriptFunctionTypeHandler = nullptr;
981989

982-
if (hasPrototype)
990+
if (noPrototypeProperty)
983991
{
984992
scriptFunctionTypeHandler = isAnonymousFunction ?
985-
this->GetDeferredAnonymousFunctionTypeHandler() : this->GetDeferredFunctionTypeHandler();
993+
this->GetDeferredAnonymousFunctionTypeHandler() :
994+
this->GetDeferredFunctionTypeHandler();
986995
}
987996
else
988997
{
989998
scriptFunctionTypeHandler = isAnonymousFunction ?
990-
JavascriptLibrary::GetDeferredAnonymousPrototypeFunctionTypeHandler() : JavascriptLibrary::GetDeferredPrototypeFunctionTypeHandler(scriptContext);
999+
JavascriptLibrary::GetDeferredAnonymousPrototypeFunctionTypeHandler() :
1000+
JavascriptLibrary::GetDeferredPrototypeFunctionTypeHandler(scriptContext);
9911001
}
9921002
return scriptFunctionTypeHandler;
9931003
}
@@ -1474,6 +1484,7 @@ namespace Js
14741484
weakMapConstructor = nullptr;
14751485
weakSetConstructor = nullptr;
14761486
generatorFunctionConstructor = nullptr;
1487+
asyncFunctionConstructor = nullptr;
14771488

14781489
if (scriptContext->GetConfig()->IsES6MapEnabled())
14791490
{
@@ -1511,6 +1522,14 @@ namespace Js
15111522
// GeneratorFunction is not a global property by ES6 spec so don't add it to the global object
15121523
}
15131524

1525+
if (scriptContext->GetConfig()->IsES7AsyncAndAwaitEnabled())
1526+
{
1527+
asyncFunctionConstructor = CreateBuiltinConstructor(&JavascriptFunction::EntryInfo::NewAsyncFunctionInstance,
1528+
DeferredTypeHandler<InitializeAsyncFunctionConstructor>::GetDefaultInstance(),
1529+
functionConstructor);
1530+
// AsyncFunction is not a global property by ES7 spec so don't add it to the global object
1531+
}
1532+
15141533
errorConstructor = CreateBuiltinConstructor(&JavascriptError::EntryInfo::NewErrorInstance,
15151534
DeferredTypeHandler<InitializeErrorConstructor>::GetDefaultInstance());
15161535
AddFunction(globalObject, PropertyIds::Error, errorConstructor);
@@ -2260,8 +2279,6 @@ namespace Js
22602279
void JavascriptLibrary::InitializeGeneratorFunctionConstructor(DynamicObject* generatorFunctionConstructor, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
22612280
{
22622281
typeHandler->Convert(generatorFunctionConstructor, mode, 3);
2263-
// Note: Any new function addition/deletion/modification should also be updated in JavascriptLibrary::ProfilerRegisterGeneratorFunction
2264-
// so that the update is in sync with profiler
22652282
JavascriptLibrary* library = generatorFunctionConstructor->GetLibrary();
22662283
ScriptContext* scriptContext = generatorFunctionConstructor->GetScriptContext();
22672284
library->AddMember(generatorFunctionConstructor, PropertyIds::length, TaggedInt::ToVarUnchecked(1), PropertyConfigurable);
@@ -2276,8 +2293,6 @@ namespace Js
22762293
void JavascriptLibrary::InitializeGeneratorFunctionPrototype(DynamicObject* generatorFunctionPrototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
22772294
{
22782295
typeHandler->Convert(generatorFunctionPrototype, mode, 3);
2279-
// Note: Any new function addition/deletion/modification should also be updated in JavascriptLibrary::ProfilerRegisterGeneratorFunction
2280-
// so that the update is in sync with profiler
22812296
JavascriptLibrary* library = generatorFunctionPrototype->GetLibrary();
22822297
ScriptContext* scriptContext = library->GetScriptContext();
22832298

@@ -2310,6 +2325,34 @@ namespace Js
23102325
generatorPrototype->SetHasNoEnumerableProperties(true);
23112326
}
23122327

2328+
void JavascriptLibrary::InitializeAsyncFunctionConstructor(DynamicObject* asyncFunctionConstructor, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
2329+
{
2330+
typeHandler->Convert(asyncFunctionConstructor, mode, 3);
2331+
JavascriptLibrary* library = asyncFunctionConstructor->GetLibrary();
2332+
ScriptContext* scriptContext = asyncFunctionConstructor->GetScriptContext();
2333+
library->AddMember(asyncFunctionConstructor, PropertyIds::length, TaggedInt::ToVarUnchecked(1), PropertyConfigurable);
2334+
library->AddMember(asyncFunctionConstructor, PropertyIds::prototype, library->asyncFunctionPrototype, PropertyNone);
2335+
if (scriptContext->GetConfig()->IsES6FunctionNameEnabled())
2336+
{
2337+
library->AddMember(asyncFunctionConstructor, PropertyIds::name, library->CreateStringFromCppLiteral(L"AsyncFunction"), PropertyConfigurable);
2338+
}
2339+
asyncFunctionConstructor->SetHasNoEnumerableProperties(true);
2340+
}
2341+
2342+
void JavascriptLibrary::InitializeAsyncFunctionPrototype(DynamicObject* asyncFunctionPrototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
2343+
{
2344+
typeHandler->Convert(asyncFunctionPrototype, mode, 2);
2345+
JavascriptLibrary* library = asyncFunctionPrototype->GetLibrary();
2346+
ScriptContext* scriptContext = library->GetScriptContext();
2347+
2348+
library->AddMember(asyncFunctionPrototype, PropertyIds::constructor, library->asyncFunctionConstructor, PropertyConfigurable);
2349+
if (scriptContext->GetConfig()->IsES6ToStringTagEnabled())
2350+
{
2351+
library->AddMember(asyncFunctionPrototype, PropertyIds::_symbolToStringTag, library->CreateStringFromCppLiteral(L"AsyncFunction"), PropertyConfigurable);
2352+
}
2353+
asyncFunctionPrototype->SetHasNoEnumerableProperties(true);
2354+
}
2355+
23132356
void JavascriptLibrary::InitializeProxyConstructor(DynamicObject* proxyConstructor, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
23142357
{
23152358
typeHandler->Convert(proxyConstructor, mode, 4);
@@ -3075,6 +3118,7 @@ namespace Js
30753118
this->promiseConstructor,
30763119
this->proxyConstructor,
30773120
this->generatorFunctionConstructor,
3121+
this->asyncFunctionConstructor,
30783122
this->errorConstructor,
30793123
this->evalErrorConstructor,
30803124
this->rangeErrorConstructor,

0 commit comments

Comments
 (0)