Skip to content

Commit

Permalink
tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Mar 25, 2020
1 parent dc3d3f0 commit 5b4d551
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 137 deletions.
15 changes: 15 additions & 0 deletions Jint.Tests.Test262/BuiltIns/PromiseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Xunit;

namespace Jint.Tests.Test262.BuiltIns
{
public class PromiseTests : Test262Test
{
[Theory(DisplayName = "built-ins\\Promise")]
[MemberData(nameof(SourceFiles), "built-ins\\Promise", false)]
[MemberData(nameof(SourceFiles), "built-ins\\Promise", true, Skip = "Skipped")]
protected void Promise(SourceFile sourceFile)
{
RunTestInternal(sourceFile);
}
}
}
18 changes: 13 additions & 5 deletions Jint.Tests.Test262/test/skipped.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
[
{
"source": "built-ins/Promise/prototype/finally/species-symbol.js",
"reason": "classes not implemented"
},
{
"source": "built-ins/Promise/prototype/finally/subclass-species-constructor-reject-count.js",
"reason": "classes not implemented"
},
{
"source": "built-ins/Promise/prototype/finally/subclass-species-constructor-resolve-count.js",
"reason": "classes not implemented"
},
{
"source": "language/expressions/assignment/fn-name-lhs-cover.js",
"reason": "Currently quite impossible to detect if assignment target is CoverParenthesizedExpression"
Expand Down Expand Up @@ -61,13 +73,9 @@
"source": "built-ins/String/raw/special-characters.js",
"reason": "Windows line ending differences"
},
{
"source": "built-ins/Symbol/species/builtin-getter-name.js",
"reason": "Promise not implemented"
},
{
"source": "language/expressions/object/method-definition/object-method-returns-promise.js",
"reason": "Promise not implemented"
"reason": "async not implemented"
},
{
"source": "built-ins/Symbol/species/subclassing.js",
Expand Down
30 changes: 24 additions & 6 deletions Jint/IteratorExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Jint.Native;
using Jint.Native.Iterator;
using Jint.Runtime;

namespace Jint
{
internal static class IteratorExtensions
{
internal static List<JsValue> CopyToList(this IteratorInstance iterator)
internal static List<JsValue> CopyToList(this IIterator iterator)
{
var items = new List<JsValue>();

var item = iterator.Next();
iterator.TryIteratorStep(out var item);

while (item.GetProperty("done").Value.AsBoolean() == false)
int i = 0;

while (!TypeConverter.ToBoolean(item.Get("done")))
{
items.Add(item.GetProperty("value").Value);
item = iterator.Next();
try
{
var jsValue = item.Get("value");
items.Add(jsValue);
}
catch
{
break;
}

iterator.TryIteratorStep(out item);

if (i++ > 1000)
{
throw new Exception("TODO this logic is still flawed");
}
}

return items;
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Global/GlobalObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static GlobalObject CreateGlobalObject(Engine engine)
}

protected override void Initialize()
{
{
const PropertyFlag lengthFlags = PropertyFlag.Configurable;
const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
var properties = new PropertyDictionary(40, checkExistingKeys: false)
Expand Down
24 changes: 19 additions & 5 deletions Jint/Native/JsValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ public bool IsDate()
return this is DateInstance;
}

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsPromise()
{
return this is PromiseInstance;
}

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsRegExp()
Expand Down Expand Up @@ -352,15 +359,12 @@ public static JsValue FromObject(Engine engine, object value)
// todo - custom task types eg ValueTask<>. Not sure these can be supported generically without writing the associated state machine code
if (value is Task task)
{
return new PromiseInstance(engine, task)
{
_prototype = engine.Promise.PrototypeObject
};
return new PromiseInstance(engine, task);
}

// if no known type could be guessed, wrap it as an ObjectInstance
var h = engine.Options._WrapObjectHandler;
ObjectInstance o = h != null ? h(value) : null;
ObjectInstance o = h?.Invoke(value);
return o ?? new ObjectWrapper(engine, value);
}

Expand Down Expand Up @@ -658,5 +662,15 @@ internal static bool SameValue(JsValue x, JsValue y)
return ReferenceEquals(x, y);
}
}

internal static IConstructor AssertConstructor(Engine engine, JsValue c)
{
if (!(c is IConstructor constructor))
{
return ExceptionHelper.ThrowTypeError<IConstructor>(engine, c + " is not a constructor");
}

return constructor;
}
}
}
150 changes: 100 additions & 50 deletions Jint/Native/Promise/PromiseConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
using System.Threading.Tasks;
using Jint.Collections;
using Jint.Native.Function;
using Jint.Native.Iterator;
using Jint.Native.Object;
using Jint.Native.Symbol;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Descriptors.Specialized;
using Jint.Runtime.Interop;

namespace Jint.Native.Promise
Expand All @@ -20,33 +20,33 @@ private PromiseConstructor(Engine engine)
{
}

public PromisePrototype PrototypeObject { get; private set; }

public static PromiseConstructor CreatePromiseConstructor(Engine engine)
{
var obj = new PromiseConstructor(engine)
{
_prototype = engine.Function.PrototypeObject
};

// The value of the [[Prototype]] internal property of the Set constructor is the Function prototype object
obj.PrototypeObject = PromisePrototype.CreatePrototypeObject(engine, obj);
obj._length = new PropertyDescriptor(0, PropertyFlag.Configurable);
obj._prototype = obj.PrototypeObject;

var obj = new PromiseConstructor(engine);
obj._prototype = PromisePrototype.CreatePrototypeObject(engine, obj);
obj._length = new PropertyDescriptor(1, PropertyFlag.Configurable);
obj._prototypeDescriptor = new PropertyDescriptor(obj._prototype, PropertyFlag.AllForbidden);
return obj;
}

protected override void Initialize()
{
var properties = new PropertyDictionary(2, checkExistingKeys: false)
const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
const PropertyFlag lengthFlags = PropertyFlag.Configurable;
var properties = new PropertyDictionary(5, checkExistingKeys: false)
{
["resolve"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "resolve", Resolve, 1), PropertyFlag.NonEnumerable)),
["reject"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "reject", Reject, 1), PropertyFlag.NonEnumerable)),
["all"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "all", All, 1), PropertyFlag.NonEnumerable)),
["race"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "race", Race, 1), PropertyFlag.NonEnumerable)),
["resolve"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "resolve", Resolve, 1, lengthFlags), propertyFlags)),
["reject"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "reject", Reject, 1, lengthFlags), propertyFlags)),
["all"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "all", All, 1, lengthFlags), propertyFlags)),
["race"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "race", Race, 1, lengthFlags), propertyFlags)),
};
SetProperties(properties);

var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", (thisObj, _) => thisObj, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
};
SetSymbols(symbols);
}

public override JsValue Call(JsValue thisObject, JsValue[] arguments)
Expand All @@ -61,45 +61,82 @@ public override JsValue Call(JsValue thisObject, JsValue[] arguments)

public ObjectInstance Construct(JsValue[] arguments, JsValue receiver)
{
FunctionInstance promiseResolver = null;

if (arguments.Length == 0 || (promiseResolver = arguments[0] as FunctionInstance) == null)
if (!(arguments.At(0) is ICallable promiseResolver))
{
ExceptionHelper.ThrowTypeError(_engine, $"Promise resolver {(arguments.Length >= 1 ? arguments[0].Type.ToString() : Undefined.ToString())} is not a function");
return ExceptionHelper.ThrowTypeError<ObjectInstance>(
_engine,
$"Promise resolver {(arguments.At(0))} is not a function");
}

var instance = new PromiseInstance(Engine, promiseResolver)
{
_prototype = PrototypeObject
};
var instance = new PromiseInstance(Engine, promiseResolver);

instance.InvokePromiseResolver();

return instance;
}

public PromiseInstance Resolve(JsValue thisRef, JsValue[] args) => PromiseInstance.CreateResolved(Engine, args.Length >= 1 ? args[0] : Undefined);
public PromiseInstance Reject(JsValue thisRef, JsValue[] args) => PromiseInstance.CreateRejected(Engine, args.Length >= 1 ? args[0] : Undefined);
private JsValue Resolve(JsValue thisObj, JsValue[] arguments)
{
if (!thisObj.IsObject())
{
ExceptionHelper.ThrowTypeError(_engine, "PromiseResolve called on non-object");
}

JsValue x = arguments.At(0);
if (x.IsPromise())
{
var xConstructor = x.Get(CommonProperties.Constructor);
if (SameValue(xConstructor, thisObj))
{
return x;
}
}

var promiseCapability = NewPromiseCapability(thisObj);
promiseCapability.Resolve(Undefined, new[] { x });
return promiseCapability;
}

public PromiseInstance All(JsValue thisRef, JsValue[] args)
private PromiseInstance Reject(JsValue thisObj, JsValue[] arguments)
{
if (args.Length == 0 || !(args[0] is ObjectInstance iteratorObj) || iteratorObj.HasProperty(GlobalSymbolRegistry.Iterator) == false)
if (!thisObj.IsObject())
{
ExceptionHelper.ThrowTypeError(Engine, $"undefined is not iterable (cannot read property {GlobalSymbolRegistry.Iterator})");
return null;
ExceptionHelper.ThrowTypeError(_engine, "PromiseReject called on non-object");
}

var iteratorCtor = iteratorObj.GetProperty(GlobalSymbolRegistry.Iterator).Value;
var iterator = iteratorCtor.Invoke(iteratorObj, new JsValue[0]) as IteratorInstance;
var r = arguments.At(0);

var promiseCapability = NewPromiseCapability(thisObj);
promiseCapability.Reject(Undefined, new[] { r });
return promiseCapability;
}

private JsValue All(JsValue thisObj, JsValue[] arguments)
{
var c = thisObj;
if (!c.IsObject())
{
ExceptionHelper.ThrowTypeError(_engine, "PromiseReject called on non-object");
}

var s = c.Get(GlobalSymbolRegistry.Species);
if (!s.IsNullOrUndefined())
{
c = s;
}

var promiseCapability = NewPromiseCapability(c);

var iterable = arguments.At(0);
var iterator = iterable.GetIterator(_engine);
var items = iterator.CopyToList();

if (items.Count == 0)
return Resolve(Undefined, new JsValue[] { Engine.Array.ConstructFast(0) });

var chainedPromise = new PromiseInstance(Engine)
{
_prototype = Engine.Promise.PrototypeObject
};
return promiseCapability.Resolve(Undefined, new JsValue[] { Engine.Array.ConstructFast(0) });
}

var chainedPromise = new PromiseInstance(Engine);

var promises = items.OfType<PromiseInstance>().ToArray();

Expand Down Expand Up @@ -150,22 +187,25 @@ public PromiseInstance All(JsValue thisRef, JsValue[] args)
return chainedPromise;
}

public PromiseInstance Race(JsValue thisRef, JsValue[] args)
private PromiseInstance Race(JsValue thisObj, JsValue[] arguments)
{
if (args.Length == 0 || !(args[0] is ObjectInstance iteratorObj) || iteratorObj.HasProperty(GlobalSymbolRegistry.Iterator) == false)
var c = thisObj;
if (!c.IsObject())
{
ExceptionHelper.ThrowTypeError(Engine, $"undefined is not iterable (cannot read property {GlobalSymbolRegistry.Iterator})");
return null;
ExceptionHelper.ThrowTypeError(_engine, "PromiseReject called on non-object");
}

var iteratorCtor = iteratorObj.GetProperty(GlobalSymbolRegistry.Iterator).Value;
var iterator = iteratorCtor.Invoke(iteratorObj, new JsValue[0]) as IteratorInstance;
var items = iterator.CopyToList();

var chainedPromise = new PromiseInstance(Engine)
var s = c.Get(GlobalSymbolRegistry.Species);
if (!s.IsNullOrUndefined())
{
_prototype = Engine.Promise.PrototypeObject
};
c = s;
}

var chainedPromise = NewPromiseCapability(c);
var iterable = arguments.At(0);
var iterator = iterable.GetIterator(_engine);

var items = iterator.CopyToList();

// If no promises passed then the spec says to pend forever!
if (items.Count == 0)
Expand Down Expand Up @@ -224,5 +264,15 @@ public PromiseInstance Race(JsValue thisRef, JsValue[] args)

return chainedPromise;
}

private PromiseInstance NewPromiseCapability(JsValue c)
{
var constructor = AssertConstructor(_engine, c);

var executor = new PromiseInstance(_engine);
var test = Construct(constructor, new JsValue[] { executor });
var promiseCapability = executor;
return promiseCapability;
}
}
}
Loading

0 comments on commit 5b4d551

Please sign in to comment.