Skip to content

Commit 8cc1487

Browse files
committed
wip-check
1 parent b88a30e commit 8cc1487

File tree

10 files changed

+221
-40
lines changed

10 files changed

+221
-40
lines changed

Jint.Tests/Runtime/GeneratorTests.cs

Lines changed: 178 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace Jint.Tests.Runtime;
33
public class GeneratorTests
44
{
55
[Fact]
6-
public void BasicYield()
6+
public void LoopYield()
77
{
88
const string Script = @"
99
const foo = function*() {
@@ -15,9 +15,185 @@ public void BasicYield()
1515
let str = '';
1616
for (const val of foo()) {
1717
str += val;
18-
}";
18+
}
19+
return str;";
1920

2021
var engine = new Engine();
2122
Assert.Equal("abc", engine.Evaluate(Script));
2223
}
24+
25+
[Fact]
26+
public void ReturnDuringYield()
27+
{
28+
const string Script = @"
29+
const foo = function*() {
30+
yield 'a';
31+
return;
32+
yield 'c';
33+
};
34+
35+
let str = '';
36+
for (const val of foo()) {
37+
str += val;
38+
}
39+
return str;";
40+
41+
var engine = new Engine();
42+
Assert.Equal("a", engine.Evaluate(Script));
43+
}
44+
45+
[Fact]
46+
public void LoneReturnInYield()
47+
{
48+
const string Script = @"
49+
const foo = function*() {
50+
return;
51+
};
52+
53+
let str = '';
54+
for (const val of foo()) {
55+
str += val;
56+
}
57+
return str;";
58+
59+
var engine = new Engine();
60+
Assert.Equal("", engine.Evaluate(Script));
61+
}
62+
63+
[Fact]
64+
public void LoneReturnValueInYield()
65+
{
66+
const string Script = @"
67+
const foo = function*() {
68+
return 'a';
69+
};
70+
71+
let str = '';
72+
for (const val of foo()) {
73+
str += val;
74+
}
75+
return str;";
76+
77+
var engine = new Engine();
78+
Assert.Equal("", engine.Evaluate(Script));
79+
}
80+
81+
[Fact]
82+
public void YieldUndefined()
83+
{
84+
const string Script = @"
85+
const foo = function*() {
86+
yield undefined;
87+
};
88+
89+
let str = '';
90+
for (const val of foo()) {
91+
str += val;
92+
}
93+
return str;";
94+
95+
var engine = new Engine();
96+
Assert.Equal("undefined", engine.Evaluate(Script));
97+
}
98+
99+
[Fact]
100+
public void ReturnUndefined()
101+
{
102+
const string Script = @"
103+
const foo = function*() {
104+
return undefined;
105+
};
106+
107+
let str = '';
108+
for (const val of foo()) {
109+
str += val;
110+
}
111+
return str;";
112+
113+
var engine = new Engine();
114+
Assert.Equal("", engine.Evaluate(Script));
115+
}
116+
117+
[Fact]
118+
public void Basic()
119+
{
120+
const string Script = @"
121+
function * generator(){
122+
yield 5; yield 6;
123+
};
124+
var iterator = generator();
125+
var item = iterator.next();
126+
var passed = item.value === 5 && item.done === false;
127+
item = iterator.next();
128+
passed &= item.value === 6 && item.done === false;
129+
item = iterator.next();
130+
passed &= item.value === void undefined && item.done === true;
131+
return passed;";
132+
133+
var engine = new Engine();
134+
Assert.True(engine.Evaluate(Script).AsBoolean());
135+
}
136+
137+
[Fact]
138+
public void FunctionExpressions()
139+
{
140+
const string Script = @"
141+
var generator = function * (){
142+
yield 5; yield 6;
143+
};
144+
var iterator = generator();
145+
var item = iterator.next();
146+
var passed = item.value === 5 && item.done === false;
147+
item = iterator.next();
148+
passed &= item.value === 6 && item.done === false;
149+
item = iterator.next();
150+
passed &= item.value === void undefined && item.done === true;
151+
return passed;";
152+
153+
var engine = new Engine();
154+
Assert.True(engine.Evaluate(Script).AsBoolean());
155+
}
156+
157+
[Fact]
158+
public void CorrectThisBinding()
159+
{
160+
const string Script = @"
161+
function * generator(){
162+
yield this.x; yield this.y;
163+
};
164+
var iterator = { g: generator, x: 5, y: 6 }.g();
165+
var item = iterator.next();
166+
var passed = item.value === 5 && item.done === false;
167+
item = iterator.next();
168+
passed &= item.value === 6 && item.done === false;
169+
item = iterator.next();
170+
passed &= item.value === void undefined && item.done === true;
171+
return passed;";
172+
173+
var engine = new Engine();
174+
Assert.True(engine.Evaluate(Script).AsBoolean());
175+
}
176+
177+
[Fact]
178+
public void Sending()
179+
{
180+
const string Script = @"
181+
var sent;
182+
function * generator(){
183+
sent = [yield 5, yield 6];
184+
};
185+
var iterator = generator();
186+
iterator.next();
187+
iterator.next(""foo"");
188+
iterator.next(""bar"");";
189+
190+
var engine = new Engine();
191+
engine.Execute(Script);
192+
193+
var sent0 = engine.Evaluate("sent[0]");
194+
var sent1 = engine.Evaluate("sent[1]");
195+
196+
Assert.Equal("foo", sent0);
197+
Assert.Equal("bar", sent1);
198+
}
23199
}

Jint/Native/Generator/GeneratorInstance.cs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal sealed class GeneratorInstance : ObjectInstance
1515
private ExecutionContext _generatorContext;
1616
private readonly JsValue? _generatorBrand = null;
1717
private JintStatementList _generatorBody = null!;
18+
public JsValue? _nextValue;
1819

1920
public GeneratorInstance(Engine engine) : base(engine)
2021
{
@@ -50,6 +51,8 @@ public JsValue GeneratorResume(JsValue value, JsValue? generatorBrand)
5051

5152
// 6. Suspend methodContext.
5253

54+
_nextValue = value;
55+
5356
var context = _engine._activeEvaluationContext;
5457
return ResumeExecution(genContext, context!);
5558
}
@@ -79,9 +82,10 @@ public JsValue GeneratorResumeAbrupt(in Completion abruptCompletion, JsValue? ge
7982
var genContext = _generatorContext;
8083
var methodContext = _engine.ExecutionContext;
8184

82-
// Suspend methodContext.
85+
// Suspend methodContext
86+
_nextValue = abruptCompletion.Value;
8387

84-
return ResumeExecution(genContext, new EvaluationContext(_engine, abruptCompletion));
88+
return ResumeExecution(genContext, new EvaluationContext(_engine));
8589
}
8690

8791
private JsValue ResumeExecution(ExecutionContext genContext, EvaluationContext context)
@@ -92,19 +96,24 @@ private JsValue ResumeExecution(ExecutionContext genContext, EvaluationContext c
9296
var result = _generatorBody.Execute(context);
9397
_engine.LeaveExecutionContext();
9498

99+
_nextValue = null;
100+
95101
ObjectInstance? resultValue = null;
96102
if (result.Type == CompletionType.Normal)
97103
{
98104
_generatorState = GeneratorState.Completed;
99-
var value = context.ResumedCompletion.GetValueOrDefault();
100-
resultValue = IteratorInstance.ValueIteratorPosition.Done(_engine, value);
105+
resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.True);
101106
}
102107
else if (result.Type == CompletionType.Return)
103108
{
104-
resultValue = new IteratorInstance.ValueIteratorPosition(_engine, result.Value, false);
105-
if (_generatorBody.Completed)
109+
if (_generatorState == GeneratorState.SuspendedYield)
110+
{
111+
resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.False);
112+
}
113+
else
106114
{
107115
_generatorState = GeneratorState.Completed;
116+
resultValue = IteratorResult.CreateValueIteratorPosition(_engine, null!, done: JsBoolean.True);
108117
}
109118
}
110119

Jint/Native/Generator/GeneratorPrototype.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ internal GeneratorPrototype(
2626

2727
protected override void Initialize()
2828
{
29-
const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
30-
const PropertyFlag lengthFlags = PropertyFlag.Configurable;
29+
const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
30+
const PropertyFlag LengthFlags = PropertyFlag.Configurable;
3131
var properties = new PropertyDictionary(4, false)
3232
{
3333
["constructor"] = new(_constructor, PropertyFlag.Configurable),
34-
["next"] = new(new ClrFunctionInstance(Engine, "next", Next, 1, lengthFlags), propertyFlags),
35-
["return"] = new(new ClrFunctionInstance(Engine, "return", Return, 1, lengthFlags), propertyFlags),
36-
["throw"] = new(new ClrFunctionInstance(Engine, "throw", Throw, 1, lengthFlags), propertyFlags)
34+
["next"] = new(new ClrFunctionInstance(Engine, "next", Next, 1, LengthFlags), PropertyFlags),
35+
["return"] = new(new ClrFunctionInstance(Engine, "return", Return, 1, LengthFlags), PropertyFlags),
36+
["throw"] = new(new ClrFunctionInstance(Engine, "throw", Throw, 1, LengthFlags), PropertyFlags)
3737
};
3838
SetProperties(properties);
3939

@@ -50,7 +50,7 @@ protected override void Initialize()
5050
private JsValue Next(JsValue thisObject, JsValue[] arguments)
5151
{
5252
var g = AssertGeneratorInstance(thisObject);
53-
var value = arguments.At(0);
53+
var value = arguments.At(0, null!);
5454
return g.GeneratorResume(value, null);
5555
}
5656

Jint/Native/Global/GlobalObject.Properties.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public partial class GlobalObject
2323
private static readonly Key propertyFloat32Array = "Float32Array";
2424
private static readonly Key propertyFloat64Array = "Float64Array";
2525
private static readonly Key propertyFunction = "Function";
26+
private static readonly Key propertyGeneratorFunction = "Generator";
2627
private static readonly Key propertyInt16Array = "Int16Array";
2728
private static readonly Key propertyInt32Array = "Int32Array";
2829
private static readonly Key propertyInt8Array = "Int8Array";
@@ -97,6 +98,7 @@ protected override void Initialize()
9798
properties.AddDangerous(propertyFloat32Array, new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Float32Array, PropertyFlags));
9899
properties.AddDangerous(propertyFloat64Array, new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Float64Array, PropertyFlags));
99100
properties.AddDangerous(propertyFunction, new PropertyDescriptor(_realm.Intrinsics.Function, PropertyFlags));
101+
properties.AddDangerous(propertyGeneratorFunction, new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.GeneratorFunction, PropertyFlags));
100102
properties.AddDangerous(propertyInt16Array, new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Int16Array, PropertyFlags));
101103
properties.AddDangerous(propertyInt32Array, new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Int32Array, PropertyFlags));
102104
properties.AddDangerous(propertyInt8Array, new LazyPropertyDescriptor(this, static state => ((GlobalObject) state!)._realm.Intrinsics.Int8Array, PropertyFlags));

Jint/Runtime/Interpreter/EvaluationContext.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ internal sealed class EvaluationContext
1010
{
1111
private readonly bool _shouldRunBeforeExecuteStatementChecks;
1212

13-
public EvaluationContext(Engine engine, in Completion? resumedCompletion = null)
13+
public EvaluationContext(Engine engine)
1414
{
1515
Engine = engine;
16-
ResumedCompletion = resumedCompletion ?? default; // TODO later
1716
OperatorOverloadingAllowed = engine.Options.Interop.AllowOperatorOverloading;
1817
_shouldRunBeforeExecuteStatementChecks = engine._constraints.Length > 0 || engine._isDebugMode;
1918
}
@@ -22,13 +21,11 @@ public EvaluationContext(Engine engine, in Completion? resumedCompletion = null)
2221
public EvaluationContext()
2322
{
2423
Engine = null!;
25-
ResumedCompletion = default; // TODO later
2624
OperatorOverloadingAllowed = false;
2725
_shouldRunBeforeExecuteStatementChecks = false;
2826
}
2927

3028
public readonly Engine Engine;
31-
public readonly Completion ResumedCompletion;
3229
public bool DebugMode => Engine._isDebugMode;
3330

3431
public SyntaxElement LastSyntaxElement

Jint/Runtime/Interpreter/Expressions/JintYieldExpression.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@ public JintYieldExpression(YieldExpression expression) : base(expression)
1515
protected override object EvaluateInternal(EvaluationContext context)
1616
{
1717
var expression = (YieldExpression) _expression;
18-
var engine = context.Engine;
1918

20-
var value = JsValue.Undefined;
21-
if (expression.Argument is not null)
19+
JsValue value;
20+
if (context.Engine.ExecutionContext.Generator?._nextValue is not null)
21+
{
22+
value = context.Engine.ExecutionContext.Generator._nextValue;
23+
}
24+
else if (expression.Argument is not null)
2225
{
2326
value = Build(expression.Argument).GetValue(context);
2427
}
28+
else
29+
{
30+
value = JsValue.Undefined;
31+
}
2532

2633
if (expression.Delegate)
2734
{

Jint/Runtime/Interpreter/JintStatementList.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ private sealed class Pair
2020

2121
private Pair[]? _jintStatements;
2222
private bool _initialized;
23-
private readonly uint _index;
23+
private uint _index;
2424
private readonly bool _generator;
2525

2626
public JintStatementList(IFunction function)
@@ -102,10 +102,13 @@ public Completion Execute(EvaluationContext context)
102102
}
103103
}
104104

105-
if (_generator && context.Engine.ExecutionContext.Generator?._generatorState == GeneratorState.SuspendedYield)
105+
if (_generator)
106106
{
107-
_index = i + 1;
108-
return new Completion(CompletionType.Return, c.Value, s._statement);
107+
if (context.Engine.ExecutionContext.Generator?._generatorState == GeneratorState.SuspendedYield)
108+
{
109+
_index = i + 1;
110+
return new Completion(CompletionType.Return, c.Value, s._statement);
111+
}
109112
}
110113

111114
if (c.Type != CompletionType.Normal)

Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ protected override void Initialize(EvaluationContext context)
1818
_lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
1919
}
2020

21-
internal override bool SupportsResume => true;
21+
internal bool SupportsResume => true;
2222

2323
/// <summary>
2424
/// Optimized for direct access without virtual dispatch.

0 commit comments

Comments
 (0)