Skip to content

Commit 79a0d87

Browse files
committed
ES6 generators
1 parent 660f6ad commit 79a0d87

23 files changed

+781
-91
lines changed

Jint.Tests.Test262/Test262Harness.settings.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"class-static-fields-public",
1616
"class-static-methods-private",
1717
"decorators",
18-
"generators",
1918
"import-assertions",
2019
"regexp-duplicate-named-groups",
2120
"regexp-lookbehind",

Jint/Engine.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Jint.Native;
55
using Jint.Native.Argument;
66
using Jint.Native.Function;
7+
using Jint.Native.Generator;
78
using Jint.Native.Object;
89
using Jint.Native.Promise;
910
using Jint.Native.Symbol;
@@ -1263,6 +1264,12 @@ internal void UpdatePrivateEnvironment(PrivateEnvironmentRecord? newEnv)
12631264
_executionContexts.ReplaceTopPrivateEnvironment(newEnv);
12641265
}
12651266

1267+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1268+
internal ref readonly ExecutionContext UpdateGenerator(GeneratorInstance generator)
1269+
{
1270+
return ref _executionContexts.ReplaceTopGenerator(generator);
1271+
}
1272+
12661273
/// <summary>
12671274
/// Invokes the named callable and returns the resulting object.
12681275
/// </summary>
@@ -1426,6 +1433,11 @@ private ObjectInstance Construct(
14261433
return result;
14271434
}
14281435

1436+
internal ref readonly ExecutionContext GetExecutionContext(int fromTop)
1437+
{
1438+
return ref _executionContexts.Peek(fromTop);
1439+
}
1440+
14291441
public void Dispose()
14301442
{
14311443
// no-op for now

Jint/Native/Function/FunctionConstructor.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,24 @@ private FunctionInstance InstantiateGeneratorFunctionObject(
115115
EnvironmentRecord scope,
116116
PrivateEnvironmentRecord? privateScope)
117117
{
118-
// TODO generators
119-
return InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope);
118+
var thisMode = functionDeclaration.Strict || _engine._isStrict
119+
? FunctionThisMode.Strict
120+
: FunctionThisMode.Global;
121+
122+
var name = functionDeclaration.Function.Id?.Name ?? "default";
123+
var F = OrdinaryFunctionCreate(
124+
_realm.Intrinsics.GeneratorFunction.PrototypeObject,
125+
functionDeclaration,
126+
thisMode,
127+
scope,
128+
privateScope);
129+
130+
F.SetFunctionName(name);
131+
132+
var prototype = OrdinaryObjectCreate(_engine, _realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
133+
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
134+
135+
return F;
120136
}
121137
}
122138
}

Jint/Native/Function/FunctionInstance.Dynamic.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Esprima.Ast;
33
using Jint.Native.Object;
44
using Jint.Runtime;
5+
using Jint.Runtime.Descriptors;
56
using Jint.Runtime.Environments;
67
using Jint.Runtime.Interpreter;
78

@@ -42,6 +43,8 @@ internal FunctionInstance CreateDynamicFunction(
4243
fallbackProto = static intrinsics => intrinsics.AsyncFunction.PrototypeObject;
4344
break;
4445
case FunctionKind.Generator:
46+
fallbackProto = static intrinsics => intrinsics.GeneratorFunction.PrototypeObject;
47+
break;
4548
case FunctionKind.AsyncGenerator:
4649
default:
4750
ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString());
@@ -157,7 +160,8 @@ internal FunctionInstance CreateDynamicFunction(
157160

158161
if (kind == FunctionKind.Generator)
159162
{
160-
ExceptionHelper.ThrowNotImplementedException("generators not implemented");
163+
var prototype = OrdinaryObjectCreate(_engine, _realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
164+
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
161165
}
162166
else if (kind == FunctionKind.AsyncGenerator)
163167
{

Jint/Native/Function/FunctionInstance.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ internal ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
323323
variableEnvironment: localEnv,
324324
_privateEnvironment,
325325
calleeRealm,
326+
generator: null,
326327
function: this);
327328

328329
// If callerContext is not already suspended, suspend callerContext.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using Jint.Native.Function;
2+
using Jint.Native.Iterator;
3+
using Jint.Native.Object;
4+
using Jint.Runtime;
5+
using Jint.Runtime.Descriptors;
6+
7+
namespace Jint.Native.Generator;
8+
9+
/// <summary>
10+
/// https://tc39.es/ecma262/#sec-generatorfunction-constructor
11+
/// </summary>
12+
internal sealed class GeneratorFunctionConstructor : FunctionInstance, IConstructor
13+
{
14+
private static readonly JsString _functionName = new("GeneratorFunction");
15+
16+
internal GeneratorFunctionConstructor(
17+
Engine engine,
18+
Realm realm,
19+
FunctionPrototype prototype,
20+
IteratorPrototype iteratorPrototype)
21+
: base(engine, realm, _functionName)
22+
{
23+
PrototypeObject = new GeneratorFunctionPrototype(engine, this, prototype, iteratorPrototype);
24+
_prototype = PrototypeObject;
25+
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
26+
_length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
27+
}
28+
29+
public GeneratorFunctionPrototype PrototypeObject { get; }
30+
31+
protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
32+
{
33+
return Construct(arguments, thisObject);
34+
}
35+
36+
public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
37+
{
38+
var function = _realm.Intrinsics.Function.CreateDynamicFunction(
39+
this,
40+
newTarget,
41+
FunctionKind.Generator,
42+
arguments);
43+
44+
return function;
45+
}
46+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Jint.Collections;
2+
using Jint.Native.Function;
3+
using Jint.Native.Iterator;
4+
using Jint.Native.Object;
5+
using Jint.Native.Symbol;
6+
using Jint.Runtime.Descriptors;
7+
8+
namespace Jint.Native.Generator;
9+
10+
/// <summary>
11+
/// https://tc39.es/ecma262/#sec-properties-of-the-generatorfunction-prototype-object
12+
/// </summary>
13+
internal sealed class GeneratorFunctionPrototype : ObjectInstance
14+
{
15+
private readonly GeneratorFunctionConstructor? _constructor;
16+
17+
internal GeneratorFunctionPrototype(
18+
Engine engine,
19+
GeneratorFunctionConstructor constructor,
20+
FunctionPrototype prototype,
21+
IteratorPrototype iteratorPrototype) : base(engine)
22+
{
23+
_constructor = constructor;
24+
_prototype = prototype;
25+
PrototypeObject = new GeneratorPrototype(engine, this, iteratorPrototype);
26+
}
27+
28+
public GeneratorPrototype PrototypeObject { get; }
29+
30+
protected override void Initialize()
31+
{
32+
var properties = new PropertyDictionary(2, checkExistingKeys: false)
33+
{
34+
["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.Configurable),
35+
["prototype"] = new PropertyDescriptor(PrototypeObject, PropertyFlag.Configurable)
36+
};
37+
SetProperties(properties);
38+
var symbols = new SymbolDictionary(1)
39+
{
40+
[GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("GeneratorFunction", PropertyFlag.Configurable)
41+
};
42+
SetSymbols(symbols);
43+
}
44+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using Jint.Native.Iterator;
2+
using Jint.Native.Object;
3+
using Jint.Runtime;
4+
using Jint.Runtime.Environments;
5+
using Jint.Runtime.Interpreter;
6+
7+
namespace Jint.Native.Generator;
8+
9+
/// <summary>
10+
/// https://tc39.es/ecma262/#sec-properties-of-generator-instances
11+
/// </summary>
12+
internal sealed class GeneratorInstance : ObjectInstance
13+
{
14+
internal GeneratorState _generatorState;
15+
private ExecutionContext _generatorContext;
16+
private readonly JsValue? _generatorBrand = null;
17+
private JintStatementList _generatorBody = null!;
18+
19+
public GeneratorInstance(Engine engine) : base(engine)
20+
{
21+
}
22+
23+
/// <summary>
24+
/// https://tc39.es/ecma262/#sec-generatorstart
25+
/// </summary>
26+
public JsValue GeneratorStart(JintStatementList generatorBody)
27+
{
28+
var genContext = _engine.UpdateGenerator(this);
29+
_generatorBody = generatorBody;
30+
31+
_generatorContext = genContext;
32+
_generatorState = GeneratorState.SuspendedStart;
33+
34+
return Undefined;
35+
}
36+
37+
/// <summary>
38+
/// https://tc39.es/ecma262/#sec-generatorresume
39+
/// </summary>
40+
public JsValue GeneratorResume(JsValue value, JsValue? generatorBrand)
41+
{
42+
var state = GeneratorValidate(generatorBrand);
43+
if (state == GeneratorState.Completed)
44+
{
45+
return new IteratorResult(_engine, value, JsBoolean.True);
46+
}
47+
48+
var genContext = _generatorContext;
49+
var methodContext = _engine.ExecutionContext;
50+
51+
// 6. Suspend methodContext.
52+
53+
var context = _engine._activeEvaluationContext;
54+
return ResumeExecution(genContext, context!);
55+
}
56+
57+
/// <summary>
58+
/// https://tc39.es/ecma262/#sec-generatorresumeabrupt
59+
/// </summary>
60+
public JsValue GeneratorResumeAbrupt(in Completion abruptCompletion, JsValue? generatorBrand)
61+
{
62+
var state = GeneratorValidate(generatorBrand);
63+
if (state == GeneratorState.SuspendedStart)
64+
{
65+
_generatorState = GeneratorState.Completed;
66+
state = GeneratorState.Completed;
67+
}
68+
69+
if (state == GeneratorState.Completed)
70+
{
71+
if (abruptCompletion.Type == CompletionType.Return)
72+
{
73+
return new IteratorResult(_engine, abruptCompletion.Value, JsBoolean.True);
74+
}
75+
76+
return abruptCompletion.Value;
77+
}
78+
79+
var genContext = _generatorContext;
80+
var methodContext = _engine.ExecutionContext;
81+
82+
// Suspend methodContext.
83+
84+
return ResumeExecution(genContext, new EvaluationContext(_engine, abruptCompletion));
85+
}
86+
87+
private JsValue ResumeExecution(ExecutionContext genContext, EvaluationContext context)
88+
{
89+
_generatorState = GeneratorState.Executing;
90+
_engine.EnterExecutionContext(genContext);
91+
92+
var result = _generatorBody.Execute(context);
93+
_engine.LeaveExecutionContext();
94+
95+
ObjectInstance? resultValue = null;
96+
if (result.Type == CompletionType.Normal)
97+
{
98+
_generatorState = GeneratorState.Completed;
99+
var value = context.ResumedCompletion.GetValueOrDefault();
100+
resultValue = IteratorInstance.ValueIteratorPosition.Done(_engine, value);
101+
}
102+
else if (result.Type == CompletionType.Return)
103+
{
104+
resultValue = new IteratorInstance.ValueIteratorPosition(_engine, result.Value, false);
105+
if (_generatorBody.Completed)
106+
{
107+
_generatorState = GeneratorState.Completed;
108+
}
109+
}
110+
111+
if (result.Type == CompletionType.Throw)
112+
{
113+
_generatorState = GeneratorState.Completed;
114+
ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
115+
}
116+
117+
return resultValue!;
118+
}
119+
120+
private GeneratorState GeneratorValidate(JsValue? generatorBrand)
121+
{
122+
if (!ReferenceEquals(generatorBrand, _generatorBrand))
123+
{
124+
ExceptionHelper.ThrowTypeError(_engine.Realm, "Generator brand differs from attached brand");
125+
}
126+
127+
if (_generatorState == GeneratorState.Executing)
128+
{
129+
ExceptionHelper.ThrowTypeError(_engine.Realm, "Generator state was unexpectedly executing");
130+
}
131+
132+
return _generatorState;
133+
}
134+
}
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
namespace Jint.Native.Generator
1+
namespace Jint.Native.Generator;
2+
3+
internal enum GeneratorKind
24
{
3-
internal enum GeneratorKind
4-
{
5-
NonGenerator,
6-
Sync,
7-
Async
8-
}
9-
}
5+
NonGenerator,
6+
Sync,
7+
Async
8+
}

0 commit comments

Comments
 (0)