Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions NiL.JS/Core/Functions/AsyncFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,26 @@ protected internal override JSValue Invoke(bool construct, JSValue targetObject,
var internalContext = new Context(_initialContext, true, this);
internalContext._callDepth = (Context.CurrentContext?._callDepth ?? 0) + 1;

// Async invocations can run concurrently on different thread-pool threads (each
// C# await after Task.Yield() can resume on a different thread). The shared
// VariableDescriptor.cacheContext/cacheValue fields on the function definition
// are not per-invocation, so concurrent calls overwrite each other's cache.
// Passing true to both initContext and initParameters forces every binding
// (arguments object, function name, and all parameters) into the per-invocation
// internalContext._variables dictionary. deepGet() then finds them via
// context._variables.TryGetValue() even after the cache has been overwritten.
// Body-variable storage is handled in CodeBlock.initVariables via the async-kind
// check that forces cew=true for all async function kinds.
// This mirrors GeneratorIterator.initContext() which also passes true for both.
initContext(
targetObject,
arguments,
_functionDefinition._functionInfo.ContainsArguments,
true,
internalContext);

initParameters(
arguments,
_functionDefinition._functionInfo.ContainsEval
|| _functionDefinition._functionInfo.ContainsWith
|| _functionDefinition._functionInfo.ContainsDebugger
|| _functionDefinition._functionInfo.NeedDecompose
|| (internalContext?._debugging ?? false),
true,
internalContext);

var result = run(internalContext);
Expand Down
2 changes: 1 addition & 1 deletion NiL.JS/Expressions/AwaitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public override JSValue Evaluate(Context context)
return null;
}

if (result == null || !result.Defined || result["then"]._valueType != JSValueType.Function)
if (result == null || !result.Defined || result.IsNull || result["then"]._valueType != JSValueType.Function)
return result;

context._executionMode = ExecutionMode.Suspend;
Expand Down
14 changes: 13 additions & 1 deletion NiL.JS/Statements/CodeBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -670,11 +670,23 @@ public override void Decompose(ref CodeNode self)
internal void initVariables(Context context)
{
var functionInfo = context._owner?._functionDefinition?._functionInfo;
var kind = context._owner?._functionDefinition?._kind;
var isAsync = kind is FunctionKind.AsyncFunction
or FunctionKind.AsyncAnonymousFunction
or FunctionKind.AsyncArrow
or FunctionKind.AsyncMethod;
// Async functions can run concurrently on different thread-pool threads, so
// their VariableDescriptor.cacheContext/cacheValue fields (shared across all
// invocations of the same function definition) are not safe to rely on.
// Force cew=true so every variable gets its own slot in the per-invocation
// context._variables dictionary, making deepGet() lookups correct even after
// concurrent invocations overwrite the shared cache fields.
var cew = functionInfo == null
|| functionInfo.ContainsEval
|| functionInfo.ContainsWith
|| functionInfo.NeedDecompose
|| functionInfo.ContainsDebugger;
|| functionInfo.ContainsDebugger
|| isAsync;
for (var i = 0; i < _variables.Length; i++)
{
var variable = _variables[i];
Expand Down
Loading