Skip to content

Commit

Permalink
ES6 generators
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed May 21, 2022
1 parent 3bc93cf commit 56f0d61
Show file tree
Hide file tree
Showing 19 changed files with 720 additions and 34 deletions.
14 changes: 0 additions & 14 deletions Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"class-static-fields-public",
"class-static-methods-private",
"FinalizationRegistry",
"generators",
"hashbang",
"import-assertions",
"import.meta",
Expand Down Expand Up @@ -138,14 +137,6 @@
"language/expressions/function/scope-name-var-open-non-strict.js",
"language/expressions/function/scope-name-var-open-strict.js",

// yield not implemented
"built-ins/TypedArrayConstructors/ctors-bigint/object-arg/as-generator-iterable-returns.js",
"built-ins/TypedArrayConstructors/ctors-bigint/object-arg/iterating-throws.js",
"language/expressions/object/accessor-name-computed-yield-id.js", // accessor / yield not implemented
"language/expressions/object/accessor-name-computed.js",
"built-ins/TypedArrayConstructors/ctors/object-arg/as-generator-iterable-returns.js",
"language/expressions/object/method-definition/name-prop-name-yield-id.js",

// accessor not implemented
"language/expressions/object/prop-dup-set-get-set.js",
"language/expressions/object/accessor-name-computed-err-to-prop-key.js",
Expand All @@ -167,11 +158,6 @@
"language/expressions/object/scope-meth-paramsbody-var-open.js",
"language/expressions/object/scope-setter-paramsbody-var-open.js",

// generators not implemented
"built-ins/Object/prototype/toString/proxy-function.js",
"language/statements/class/subclass/builtin-objects/GeneratorFunction/*.js",
"language/**/*-yield-*.js",

// JavaScriptParser cannot handle direct 'super.property' script code
"language/expressions/super/prop-dot-cls-val-from-eval.js",
"language/expressions/super/prop-dot-obj-val-from-eval.js",
Expand Down
12 changes: 12 additions & 0 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,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 @@ -1237,6 +1238,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 @@ -1383,6 +1390,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
25 changes: 22 additions & 3 deletions Jint/Native/Function/FunctionConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ internal FunctionInstance CreateDynamicFunction(
fallbackProto = static intrinsics => intrinsics.Function.PrototypeObject;
break;
case FunctionKind.Generator:
fallbackProto = static intrinsics => intrinsics.GeneratorFunction.PrototypeObject;
break;
case FunctionKind.AsyncGenerator:
case FunctionKind.Async:
default:
Expand Down Expand Up @@ -194,7 +196,8 @@ internal FunctionInstance CreateDynamicFunction(

if (kind == FunctionKind.Generator)
{
ExceptionHelper.ThrowNotImplementedException("generators not implemented");
var prototype = OrdinaryObjectCreate(_realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
}
else if (kind == FunctionKind.AsyncGenerator)
{
Expand Down Expand Up @@ -275,8 +278,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(_realm.Intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
F.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));

return F;
}
}
}
1 change: 1 addition & 0 deletions Jint/Native/Function/FunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ internal ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
variableEnvironment: localEnv,
_privateEnvironment,
calleeRealm,
generator: null,
function: this);

// If callerContext is not already suspended, suspend callerContext.
Expand Down
47 changes: 47 additions & 0 deletions Jint/Native/Generator/GeneratorFunctionConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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>
public 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;
}
}
}
45 changes: 45 additions & 0 deletions Jint/Native/Generator/GeneratorFunctionPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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>
public 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);
}
}
}
135 changes: 135 additions & 0 deletions Jint/Native/Generator/GeneratorInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
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;

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;
}
}
}
Loading

0 comments on commit 56f0d61

Please sign in to comment.