Skip to content

Commit

Permalink
Improve array interop API (#1352)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Nov 9, 2022
1 parent c680830 commit 6b5f04d
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 71 deletions.
2 changes: 1 addition & 1 deletion Jint.Tests.PublicInterface/RavenApiUsageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void CanBuildCustomScriptFunctionInstance()

var properties = new List<Node>
{
new Property(PropertyKind.Data, new Identifier("field"), false,
new Property(PropertyKind.Init, new Identifier("field"), false,
new StaticMemberExpression(new Identifier("self"), new Identifier("field"), optional: false), false, false)
};

Expand Down
133 changes: 85 additions & 48 deletions Jint/Native/Array/ArrayInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public ArrayInstance(Engine engine, uint capacity = 0, uint length = 0) : base(e

/// <summary>
/// Possibility to construct valid array fast.
/// Requires that supplied array is of type object[] and it should only contain values inheriting from JsValue.
/// Requires that supplied array is of type object[] and it should only contain values that inherit from JsValue or PropertyDescriptor or are null.
/// The array will be owned and modified by Jint afterwards.
/// </summary>
public ArrayInstance(Engine engine, object[] items) : base(engine)
Expand All @@ -72,7 +72,7 @@ public ArrayInstance(Engine engine, object[] items) : base(engine)
{
if (items.GetType() != typeof(object[]))
{
ExceptionHelper.ThrowArgumentException("Supplied array must be of type object[] and should only contain values inheriting from JsValue");
ExceptionHelper.ThrowArgumentException("Supplied array must be of type object[] and should only contain values that inherit from JsValue or PropertyDescriptor or are null");
}

_dense = items;
Expand Down Expand Up @@ -394,45 +394,9 @@ public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.None
/// <param name="includeLength">Whether to return length and it's value.</param>
public IEnumerable<KeyValuePair<string, JsValue>> GetEntries(bool includeLength = false)
{
var temp = _dense;
if (temp != null)
{
var length = System.Math.Min(temp.Length, GetLength());
for (var i = 0; i < length; i++)
{
var value = temp[i];
if (value != null)
{
var key = TypeConverter.ToString(i);
if (value is not PropertyDescriptor descriptor)
{
yield return new KeyValuePair<string, JsValue>(key, (JsValue) value);
}
else
{
yield return new KeyValuePair<string, JsValue>(key, descriptor.Value);
}
}
}
}
else
foreach (var (index, value) in this.Enumerate())
{
foreach (var entry in _sparse!)
{
var value = entry.Value;
if (value is not null)
{
var key = TypeConverter.ToString(entry.Key);
if (value is not PropertyDescriptor descriptor)
{
yield return new KeyValuePair<string, JsValue>(key, (JsValue) value);
}
else
{
yield return new KeyValuePair<string, JsValue>(key, descriptor.Value);
}
}
}
yield return new KeyValuePair<string, JsValue>(TypeConverter.ToString(index), value);
}

if (includeLength && _length != null)
Expand Down Expand Up @@ -954,25 +918,80 @@ public JsValue[] ToArray()
for (uint i = 0; i < length; i++)
{
TryGetValue(i, out var outValue);
array[i] = outValue ?? Undefined;
array[i] = outValue;
}

return array;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public IEnumerator<JsValue> GetEnumerator()
{
var length = GetLength();
for (uint i = 0; i < length; i++)
foreach (var (_, value) in this.Enumerate())
{
TryGetValue(i, out var outValue);
yield return outValue ?? Undefined;
yield return value;
}
}

IEnumerator IEnumerable.GetEnumerator()
private readonly record struct IndexedEntry(int Index, JsValue Value);

private IEnumerable<IndexedEntry> Enumerate()
{
return GetEnumerator();
if (!CanUseFastAccess)
{
// slow path where prototype is also checked
var length = GetLength();
for (uint i = 0; i < length; i++)
{
TryGetValue(i, out var outValue);
yield return new IndexedEntry((int) i, outValue);
}

yield break;
}

var temp = _dense;
if (temp != null)
{
var length = System.Math.Min(temp.Length, GetLength());
for (var i = 0; i < length; i++)
{
var value = temp[i];
if (value != null)
{
if (value is not PropertyDescriptor descriptor)
{
yield return new IndexedEntry(i, (JsValue) value);
}
else
{
yield return new IndexedEntry(i, descriptor.Value);
}
}
}
}
else
{
foreach (var entry in _sparse!)
{
var value = entry.Value;
if (value is not null)
{
if (value is not PropertyDescriptor descriptor)
{
yield return new IndexedEntry((int) entry.Key, (JsValue) value);
}
else
{
yield return new IndexedEntry((int) entry.Key, descriptor.Value);
}
}
}
}
}

/// <summary>
Expand Down Expand Up @@ -1195,7 +1214,25 @@ internal ArrayInstance Map(JsValue[] arguments)
get
{
TryGetValue(index, out var kValue);
return kValue ?? Undefined;
return kValue;
}
}

public JsValue this[int index]
{
get
{
JsValue? kValue;
if (index >= 0)
{
TryGetValue((uint) index, out kValue);
}
else
{
// slow path
TryGetValue(JsNumber.Create(index), out kValue);
}
return kValue;
}
}

Expand Down
2 changes: 0 additions & 2 deletions Jint/Native/Array/ArrayPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1135,8 +1135,6 @@ private JsValue Slice(JsValue thisObj, JsValue[] arguments)
}
}
}
a.DefineOwnProperty(CommonProperties.Length, new PropertyDescriptor(length, PropertyFlag.ConfigurableEnumerableWritable));

return a;
}

Expand Down
30 changes: 28 additions & 2 deletions Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ namespace Jint.Runtime.Interpreter.Expressions
{
internal sealed class JintArrayExpression : JintExpression
{
private JintExpression[] _expressions = Array.Empty<JintExpression>();
private JintExpression?[] _expressions = Array.Empty<JintExpression?>();
private bool _hasSpreads;

public JintArrayExpression(ArrayExpression expression) : base(expression)
private JintArrayExpression(ArrayExpression expression) : base(expression)
{
_initialized = false;
}

public static JintExpression Build(ArrayExpression expression)
{
return expression.Elements.Count == 0
? JintEmptyArrayExpression.Instance
: new JintArrayExpression(expression);
}

protected override void Initialize(EvaluationContext context)
{
ref readonly var elements = ref ((ArrayExpression) _expression).Elements;
Expand Down Expand Up @@ -104,5 +111,24 @@ protected override void ProcessItem(JsValue[] args, JsValue currentValue)
_instance.SetIndexValue((uint) _index, currentValue, updateLength: false);
}
}

internal sealed class JintEmptyArrayExpression : JintExpression
{
public static JintEmptyArrayExpression Instance = new(new ArrayExpression(NodeList.Create(Enumerable.Empty<Expression?>())));

private JintEmptyArrayExpression(Expression expression) : base(expression)
{
}

protected override object EvaluateInternal(EvaluationContext context)
{
return new ArrayInstance(context.Engine, Array.Empty<object>());
}

public override JsValue GetValue(EvaluationContext context)
{
return new ArrayInstance(context.Engine, Array.Empty<object>());
}
}
}
}
4 changes: 2 additions & 2 deletions Jint/Runtime/Interpreter/Expressions/JintExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ protected internal static JintExpression Build(Expression expression)
var result = expression.Type switch
{
Nodes.AssignmentExpression => JintAssignmentExpression.Build((AssignmentExpression) expression),
Nodes.ArrayExpression => new JintArrayExpression((ArrayExpression) expression),
Nodes.ArrayExpression => JintArrayExpression.Build((ArrayExpression) expression),
Nodes.ArrowFunctionExpression => new JintArrowFunctionExpression((ArrowFunctionExpression) expression),
Nodes.BinaryExpression => JintBinaryExpression.Build((BinaryExpression) expression),
Nodes.CallExpression => new JintCallExpression((CallExpression) expression),
Expand All @@ -123,7 +123,7 @@ protected internal static JintExpression Build(Expression expression)
},
Nodes.MemberExpression => new JintMemberExpression((MemberExpression) expression),
Nodes.NewExpression => new JintNewExpression((NewExpression) expression),
Nodes.ObjectExpression => new JintObjectExpression((ObjectExpression) expression),
Nodes.ObjectExpression => JintObjectExpression.Build((ObjectExpression) expression),
Nodes.SequenceExpression => new JintSequenceExpression((SequenceExpression) expression),
Nodes.ThisExpression => new JintThisExpression((ThisExpression) expression),
Nodes.UpdateExpression => new JintUpdateExpression((UpdateExpression) expression),
Expand Down
50 changes: 34 additions & 16 deletions Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,31 @@ public JintFunctionDefinition GetFunctionDefinition(Engine engine)
}
}

public JintObjectExpression(ObjectExpression expression) : base(expression)
private JintObjectExpression(ObjectExpression expression) : base(expression)
{
_initialized = false;
}

public static JintExpression Build(ObjectExpression expression)
{
return expression.Properties.Count == 0
? JintEmptyObjectExpression.Instance
: new JintObjectExpression(expression);
}

protected override void Initialize(EvaluationContext context)
{
_canBuildFast = true;
var expression = (ObjectExpression) _expression;
if (expression.Properties.Count == 0)
{
// empty object initializer
return;
}
ref readonly var properties = ref expression.Properties;

var engine = context.Engine;
_valueExpressions = new JintExpression[expression.Properties.Count];
_properties = new ObjectProperty[expression.Properties.Count];
_valueExpressions = new JintExpression[properties.Count];
_properties = new ObjectProperty[properties.Count];

for (var i = 0; i < _properties.Length; i++)
{
string? propName = null;
var property = expression.Properties[i];
var property = properties[i];
if (property is Property p)
{
if (p.Key is Literal literal)
Expand Down Expand Up @@ -128,12 +130,7 @@ protected override object EvaluateInternal(EvaluationContext context)
/// </summary>
private object BuildObjectFast(EvaluationContext context)
{
var obj = context.Engine.Realm.Intrinsics.Object.Construct(0);
if (_properties.Length == 0)
{
return obj;
}

var obj = new ObjectInstance(context.Engine);
var properties = new PropertyDictionary(_properties.Length, checkExistingKeys: true);
for (var i = 0; i < _properties.Length; i++)
{
Expand Down Expand Up @@ -244,5 +241,26 @@ private object BuildObjectNormal(EvaluationContext context)

return obj;
}

internal sealed class JintEmptyObjectExpression : JintExpression
{
public static JintEmptyObjectExpression Instance = new(new ObjectExpression(NodeList.Create(Enumerable.Empty<Node>())));

private JintEmptyObjectExpression(Expression expression) : base(expression)
{
}

protected override object EvaluateInternal(EvaluationContext context)
{
return new ObjectInstance(context.Engine);
}

public override JsValue GetValue(EvaluationContext context)
{
return new ObjectInstance(context.Engine);
}
}
}
}


0 comments on commit 6b5f04d

Please sign in to comment.