Skip to content

Commit 9a4f5b2

Browse files
committed
ES6 generators
1 parent 97e481f commit 9a4f5b2

21 files changed

+739
-36
lines changed

Jint.Tests.Test262/Test262Harness.settings.json

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"class-static-fields-public",
1717
"class-static-methods-private",
1818
"FinalizationRegistry",
19-
"generators",
2019
"import-assertions",
2120
"Promise.allSettled",
2221
"Promise.any",
@@ -134,14 +133,6 @@
134133
"language/expressions/function/scope-name-var-open-non-strict.js",
135134
"language/expressions/function/scope-name-var-open-strict.js",
136135

137-
// yield not implemented
138-
"built-ins/TypedArrayConstructors/ctors-bigint/object-arg/as-generator-iterable-returns.js",
139-
"built-ins/TypedArrayConstructors/ctors-bigint/object-arg/iterating-throws.js",
140-
"language/expressions/object/accessor-name-computed-yield-id.js", // accessor / yield not implemented
141-
"language/expressions/object/accessor-name-computed.js",
142-
"built-ins/TypedArrayConstructors/ctors/object-arg/as-generator-iterable-returns.js",
143-
"language/expressions/object/method-definition/name-prop-name-yield-id.js",
144-
145136
// accessor not implemented
146137
"language/expressions/object/prop-dup-set-get-set.js",
147138
"language/expressions/object/accessor-name-computed-err-to-prop-key.js",
@@ -163,11 +154,6 @@
163154
"language/expressions/object/scope-meth-paramsbody-var-open.js",
164155
"language/expressions/object/scope-setter-paramsbody-var-open.js",
165156

166-
// generators not implemented
167-
"built-ins/Object/prototype/toString/proxy-function.js",
168-
"language/statements/class/subclass/builtin-objects/GeneratorFunction/*.js",
169-
"language/**/*-yield-*.js",
170-
171157
// JavaScriptParser cannot handle direct 'super.property' script code
172158
"language/expressions/super/prop-dot-cls-val-from-eval.js",
173159
"language/expressions/super/prop-dot-obj-val-from-eval.js",

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;
@@ -1240,6 +1241,12 @@ internal void UpdatePrivateEnvironment(PrivateEnvironmentRecord? newEnv)
12401241
_executionContexts.ReplaceTopPrivateEnvironment(newEnv);
12411242
}
12421243

1244+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1245+
internal ref readonly ExecutionContext UpdateGenerator(GeneratorInstance generator)
1246+
{
1247+
return ref _executionContexts.ReplaceTopGenerator(generator);
1248+
}
1249+
12431250
/// <summary>
12441251
/// Invokes the named callable and returns the resulting object.
12451252
/// </summary>
@@ -1398,6 +1405,11 @@ private ObjectInstance Construct(
13981405
return result;
13991406
}
14001407

1408+
internal ref readonly ExecutionContext GetExecutionContext(int fromTop)
1409+
{
1410+
return ref _executionContexts.Peek(fromTop);
1411+
}
1412+
14011413
public void Dispose()
14021414
{
14031415
// no-op for now

Jint/Native/Function/FunctionConstructor.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ internal FunctionInstance CreateDynamicFunction(
7777
fallbackProto = static intrinsics => intrinsics.Function.PrototypeObject;
7878
break;
7979
case FunctionKind.Generator:
80+
fallbackProto = static intrinsics => intrinsics.GeneratorFunction.PrototypeObject;
81+
break;
8082
case FunctionKind.AsyncGenerator:
8183
case FunctionKind.Async:
8284
default:
@@ -193,7 +195,8 @@ internal FunctionInstance CreateDynamicFunction(
193195

194196
if (kind == FunctionKind.Generator)
195197
{
196-
ExceptionHelper.ThrowNotImplementedException("generators not implemented");
198+
var prototype = OrdinaryObjectCreate(_realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
199+
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
197200
}
198201
else if (kind == FunctionKind.AsyncGenerator)
199202
{
@@ -274,8 +277,24 @@ private FunctionInstance InstantiateGeneratorFunctionObject(
274277
EnvironmentRecord scope,
275278
PrivateEnvironmentRecord? privateScope)
276279
{
277-
// TODO generators
278-
return InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope);
280+
var thisMode = functionDeclaration.Strict || _engine._isStrict
281+
? FunctionThisMode.Strict
282+
: FunctionThisMode.Global;
283+
284+
var name = functionDeclaration.Function.Id?.Name ?? "default";
285+
var F = OrdinaryFunctionCreate(
286+
_realm.Intrinsics.GeneratorFunction.PrototypeObject,
287+
functionDeclaration,
288+
thisMode,
289+
scope,
290+
privateScope);
291+
292+
F.SetFunctionName(name);
293+
294+
var prototype =OrdinaryObjectCreate(_realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
295+
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
296+
297+
return F;
279298
}
280299
}
281300
}

Jint/Native/Function/FunctionInstance.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ internal ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
343343
variableEnvironment: localEnv,
344344
_privateEnvironment,
345345
calleeRealm,
346+
generator: null,
346347
function: this);
347348

348349
// If callerContext is not already suspended, suspend callerContext.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
public 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+
}
47+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
public 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+
}
45+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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+
}
135+
}

0 commit comments

Comments
 (0)