Skip to content

Commit

Permalink
ES6 generators
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Oct 24, 2022
1 parent 660f6ad commit 09c4148
Show file tree
Hide file tree
Showing 23 changed files with 775 additions and 76 deletions.
1 change: 0 additions & 1 deletion Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"class-static-fields-public",
"class-static-methods-private",
"decorators",
"generators",
"import-assertions",
"regexp-duplicate-named-groups",
"regexp-lookbehind",
Expand Down
12 changes: 12 additions & 0 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Jint.Native;
using Jint.Native.Argument;
using Jint.Native.Function;
using Jint.Native.Generator;
using Jint.Native.Object;
using Jint.Native.Promise;
using Jint.Native.Symbol;
Expand Down Expand Up @@ -1263,6 +1264,12 @@ internal void UpdatePrivateEnvironment(PrivateEnvironmentRecord? newEnv)
_executionContexts.ReplaceTopPrivateEnvironment(newEnv);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref readonly ExecutionContext UpdateGenerator(GeneratorInstance generator)
{
return ref _executionContexts.ReplaceTopGenerator(generator);
}

/// <summary>
/// Invokes the named callable and returns the resulting object.
/// </summary>
Expand Down Expand Up @@ -1426,6 +1433,11 @@ private ObjectInstance Construct(
return result;
}

internal ref readonly ExecutionContext GetExecutionContext(int fromTop)
{
return ref _executionContexts.Peek(fromTop);
}

public void Dispose()
{
// no-op for now
Expand Down
20 changes: 18 additions & 2 deletions Jint/Native/Function/FunctionConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,24 @@ private FunctionInstance InstantiateGeneratorFunctionObject(
EnvironmentRecord scope,
PrivateEnvironmentRecord? privateScope)
{
// TODO generators
return InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope);
var thisMode = functionDeclaration.Strict || _engine._isStrict
? FunctionThisMode.Strict
: FunctionThisMode.Global;

var name = functionDeclaration.Function.Id?.Name ?? "default";
var F = OrdinaryFunctionCreate(
_realm.Intrinsics.GeneratorFunction.PrototypeObject,
functionDeclaration,
thisMode,
scope,
privateScope);

F.SetFunctionName(name);

var prototype = OrdinaryObjectCreate(_engine, _realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));

return F;
}
}
}
6 changes: 5 additions & 1 deletion Jint/Native/Function/FunctionInstance.Dynamic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Esprima.Ast;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Environments;
using Jint.Runtime.Interpreter;

Expand Down Expand Up @@ -42,6 +43,8 @@ internal FunctionInstance CreateDynamicFunction(
fallbackProto = static intrinsics => intrinsics.AsyncFunction.PrototypeObject;
break;
case FunctionKind.Generator:
fallbackProto = static intrinsics => intrinsics.GeneratorFunction.PrototypeObject;
break;
case FunctionKind.AsyncGenerator:
default:
ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString());
Expand Down Expand Up @@ -157,7 +160,8 @@ internal FunctionInstance CreateDynamicFunction(

if (kind == FunctionKind.Generator)
{
ExceptionHelper.ThrowNotImplementedException("generators not implemented");
var prototype = OrdinaryObjectCreate(_engine, _realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
}
else if (kind == FunctionKind.AsyncGenerator)
{
Expand Down
1 change: 1 addition & 0 deletions Jint/Native/Function/FunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ internal ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
variableEnvironment: localEnv,
_privateEnvironment,
calleeRealm,
generator: null,
function: this);

// If callerContext is not already suspended, suspend callerContext.
Expand Down
46 changes: 46 additions & 0 deletions Jint/Native/Generator/GeneratorFunctionConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Jint.Native.Function;
using Jint.Native.Iterator;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;

namespace Jint.Native.Generator;

/// <summary>
/// https://tc39.es/ecma262/#sec-generatorfunction-constructor
/// </summary>
internal sealed class GeneratorFunctionConstructor : FunctionInstance, IConstructor
{
private static readonly JsString _functionName = new("GeneratorFunction");

internal GeneratorFunctionConstructor(
Engine engine,
Realm realm,
FunctionPrototype prototype,
IteratorPrototype iteratorPrototype)
: base(engine, realm, _functionName)
{
PrototypeObject = new GeneratorFunctionPrototype(engine, this, prototype, iteratorPrototype);
_prototype = PrototypeObject;
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
_length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
}

public GeneratorFunctionPrototype PrototypeObject { get; }

protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
return Construct(arguments, thisObject);
}

public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
var function = _realm.Intrinsics.Function.CreateDynamicFunction(
this,
newTarget,
FunctionKind.Generator,
arguments);

return function;
}
}
44 changes: 44 additions & 0 deletions Jint/Native/Generator/GeneratorFunctionPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Jint.Collections;
using Jint.Native.Function;
using Jint.Native.Iterator;
using Jint.Native.Object;
using Jint.Native.Symbol;
using Jint.Runtime.Descriptors;

namespace Jint.Native.Generator;

/// <summary>
/// https://tc39.es/ecma262/#sec-properties-of-the-generatorfunction-prototype-object
/// </summary>
internal sealed class GeneratorFunctionPrototype : ObjectInstance
{
private readonly GeneratorFunctionConstructor? _constructor;

internal GeneratorFunctionPrototype(
Engine engine,
GeneratorFunctionConstructor constructor,
FunctionPrototype prototype,
IteratorPrototype iteratorPrototype) : base(engine)
{
_constructor = constructor;
_prototype = prototype;
PrototypeObject = new GeneratorPrototype(engine, this, iteratorPrototype);
}

public GeneratorPrototype PrototypeObject { get; }

protected override void Initialize()
{
var properties = new PropertyDictionary(2, checkExistingKeys: false)
{
["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.Configurable),
["prototype"] = new PropertyDescriptor(PrototypeObject, PropertyFlag.Configurable)
};
SetProperties(properties);
var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("GeneratorFunction", PropertyFlag.Configurable)
};
SetSymbols(symbols);
}
}
134 changes: 134 additions & 0 deletions Jint/Native/Generator/GeneratorInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using Jint.Native.Iterator;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Environments;
using Jint.Runtime.Interpreter;

namespace Jint.Native.Generator;

/// <summary>
/// https://tc39.es/ecma262/#sec-properties-of-generator-instances
/// </summary>
internal sealed class GeneratorInstance : ObjectInstance
{
internal GeneratorState _generatorState;
private ExecutionContext _generatorContext;
private readonly JsValue? _generatorBrand = null;
private JintStatementList _generatorBody = null!;

public GeneratorInstance(Engine engine) : base(engine)
{
}

/// <summary>
/// https://tc39.es/ecma262/#sec-generatorstart
/// </summary>
public JsValue GeneratorStart(JintStatementList generatorBody)
{
var genContext = _engine.UpdateGenerator(this);
_generatorBody = generatorBody;

_generatorContext = genContext;
_generatorState = GeneratorState.SuspendedStart;

return Undefined;
}

/// <summary>
/// https://tc39.es/ecma262/#sec-generatorresume
/// </summary>
public JsValue GeneratorResume(JsValue value, JsValue? generatorBrand)
{
var state = GeneratorValidate(generatorBrand);
if (state == GeneratorState.Completed)
{
return new IteratorResult(_engine, value, JsBoolean.True);
}

var genContext = _generatorContext;
var methodContext = _engine.ExecutionContext;

// 6. Suspend methodContext.

var context = _engine._activeEvaluationContext;
return ResumeExecution(genContext, context!);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-generatorresumeabrupt
/// </summary>
public JsValue GeneratorResumeAbrupt(in Completion abruptCompletion, JsValue? generatorBrand)
{
var state = GeneratorValidate(generatorBrand);
if (state == GeneratorState.SuspendedStart)
{
_generatorState = GeneratorState.Completed;
state = GeneratorState.Completed;
}

if (state == GeneratorState.Completed)
{
if (abruptCompletion.Type == CompletionType.Return)
{
return new IteratorResult(_engine, abruptCompletion.Value, JsBoolean.True);
}

return abruptCompletion.Value;
}

var genContext = _generatorContext;
var methodContext = _engine.ExecutionContext;

// Suspend methodContext.

return ResumeExecution(genContext, new EvaluationContext(_engine, abruptCompletion));
}

private JsValue ResumeExecution(ExecutionContext genContext, EvaluationContext context)
{
_generatorState = GeneratorState.Executing;
_engine.EnterExecutionContext(genContext);

var result = _generatorBody.Execute(context);
_engine.LeaveExecutionContext();

ObjectInstance? resultValue = null;
if (result.Type == CompletionType.Normal)
{
_generatorState = GeneratorState.Completed;
var value = context.ResumedCompletion.GetValueOrDefault();
resultValue = IteratorInstance.ValueIteratorPosition.Done(_engine, value);
}
else if (result.Type == CompletionType.Return)
{
resultValue = new IteratorInstance.ValueIteratorPosition(_engine, result.Value, false);
if (_generatorBody.Completed)
{
_generatorState = GeneratorState.Completed;
}
}

if (result.Type == CompletionType.Throw)
{
_generatorState = GeneratorState.Completed;
ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
}

return resultValue!;
}

private GeneratorState GeneratorValidate(JsValue? generatorBrand)
{
if (!ReferenceEquals(generatorBrand, _generatorBrand))
{
ExceptionHelper.ThrowTypeError(_engine.Realm, "Generator brand differs from attached brand");
}

if (_generatorState == GeneratorState.Executing)
{
ExceptionHelper.ThrowTypeError(_engine.Realm, "Generator state was unexpectedly executing");
}

return _generatorState;
}
}
15 changes: 7 additions & 8 deletions Jint/Native/Generator/GeneratorKind.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
namespace Jint.Native.Generator
namespace Jint.Native.Generator;

internal enum GeneratorKind
{
internal enum GeneratorKind
{
NonGenerator,
Sync,
Async
}
}
NonGenerator,
Sync,
Async
}
Loading

0 comments on commit 09c4148

Please sign in to comment.