Skip to content

Commit

Permalink
Add protected function instance constructor (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceice committed Jul 19, 2020
1 parent 68733e1 commit 5d1114d
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 5 deletions.
25 changes: 25 additions & 0 deletions Jint.Tests/Runtime/Domain/JsUuid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Jint.Native;

namespace Jint.Tests.Runtime.Domain
{
public sealed class JsUuid : JsValue, IEquatable<JsUuid>
{
internal readonly Guid _value;
public static readonly JsUuid Empty = new JsUuid(Guid.Empty);

public JsUuid(Guid value) : base(Jint.Runtime.Types.String) => _value = value;

public static implicit operator JsUuid(Guid g) => new JsUuid(g);

public override bool Equals(JsValue other) => Equals(other as JsUuid);

public bool Equals(JsUuid other) => other?._value == _value;

public override int GetHashCode() => _value.GetHashCode();

public override object ToObject() => _value;

public override string ToString() => _value.ToString();
}
}
70 changes: 70 additions & 0 deletions Jint.Tests/Runtime/Domain/UuidConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using Jint.Native;
using Jint.Native.Function;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;

namespace Jint.Tests.Runtime.Domain
{
internal sealed class UuidConstructor : FunctionInstance, IConstructor
{
private static readonly JsString _functionName = new JsString("Uuid");

private UuidConstructor(Engine engine) : base(engine, _functionName)
{
}

private JsValue Parse(JsValue @this, JsValue[] arguments)
{
switch (arguments.At(0))
{
case JsUuid uid:
return Construct(uid);

case JsValue js when Guid.TryParse(js.AsString(), out var res):
return Construct(res);
}

return Undefined;
}

protected override ObjectInstance GetPrototypeOf() => _prototype;

internal ObjectInstance _prototype;

public UuidPrototype PrototypeObject { get; private set; }

public static UuidConstructor CreateUuidConstructor(Engine engine)
{
var obj = new UuidConstructor(engine)
{
// The value of the [[Prototype]] internal property of the Uuid constructor is the Function prototype object
_prototype = engine.Function.PrototypeObject
};
obj.PrototypeObject = UuidPrototype.CreatePrototypeObject(engine, obj);

// The initial value of Uuid.prototype is the Date prototype object
obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, false, false, false));

engine.SetValue("Uuid", obj);
obj.Configure();
obj.PrototypeObject.Configure();

return obj;
}

public override JsValue Call(JsValue thisObject, JsValue[] arguments) => Construct(arguments, null);

public void Configure()
{
FastAddProperty("parse", new ClrFunctionInstance(Engine, "parse", Parse), true, false, true);
FastAddProperty("Empty", JsUuid.Empty, true, false, true);
}

public UuidInstance Construct(JsUuid uuid) => new UuidInstance(Engine) { PrimitiveValue = uuid, _prototype = PrototypeObject };

public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) => Construct(Guid.NewGuid());
}
}
26 changes: 26 additions & 0 deletions Jint.Tests/Runtime/Domain/UuidConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Jint.Native;
using Jint.Runtime.Interop;
using System;

namespace Jint.Tests.Runtime.Domain
{
public class UuidConverter : IObjectConverter
{
internal UuidConverter()
{
}

public bool TryConvert(Engine engine, object value, out JsValue result)
{
switch (value)
{
case Guid g:
result = new JsUuid(g);
return true;
}

result = JsValue.Undefined;
return false;
}
}
}
20 changes: 20 additions & 0 deletions Jint.Tests/Runtime/Domain/UuidInstance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Jint.Native.Object;
using Jint.Runtime.Interop;

namespace Jint.Tests.Runtime.Domain
{
internal class UuidInstance : ObjectInstance, IObjectWrapper
{
protected override ObjectInstance GetPrototypeOf() => _prototype;

internal ObjectInstance _prototype;

public JsUuid PrimitiveValue { get; set; }

public object Target => PrimitiveValue?._value;

public UuidInstance(Engine engine) : base(engine)
{
}
}
}
44 changes: 44 additions & 0 deletions Jint.Tests/Runtime/Domain/UuidPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Jint.Native;
using Jint.Runtime;
using Jint.Runtime.Interop;

namespace Jint.Tests.Runtime.Domain
{
internal sealed class UuidPrototype : UuidInstance
{
private UuidPrototype(Engine engine) : base(engine)
{
}

private UuidInstance EnsureUuidInstance(JsValue thisObj)
{
return thisObj.TryCast<UuidInstance>(value =>
{
throw new JavaScriptException(Engine.TypeError, "Invalid Uuid");
});
}

private JsValue ToGuidString(JsValue thisObj, JsValue[] arguments) => EnsureUuidInstance(thisObj).PrimitiveValue.ToString();

private JsValue ValueOf(JsValue thisObj, JsValue[] arguments) => EnsureUuidInstance(thisObj).PrimitiveValue;

public static UuidPrototype CreatePrototypeObject(Engine engine, UuidConstructor ctor)
{
var obj = new UuidPrototype(engine)
{
PrimitiveValue = JsUuid.Empty,
_prototype = engine.Object.PrototypeObject,
};

obj.FastAddProperty("constructor", ctor, false, false, true);

return obj;
}

public void Configure()
{
FastAddProperty("toString", new ClrFunctionInstance(Engine, "toString", ToGuidString), true, false, true);
FastAddProperty("valueOf", new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true);
}
}
}
50 changes: 50 additions & 0 deletions Jint.Tests/Runtime/UuidTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Jint.Tests.Runtime.Domain;
using System;
using Xunit;

namespace Jint.Tests.Runtime
{
public class UuidTests : IDisposable
{
private readonly Engine _engine;

public UuidTests()
{
_engine = new Engine(o => o.AddObjectConverter(new UuidConverter()))
.SetValue("copy", new Func<Guid, Guid>(v => new Guid(v.ToByteArray())))
;
UuidConstructor.CreateUuidConstructor(_engine);
}

void IDisposable.Dispose()
{
}

private object RunTest(string source)
{
return _engine.Execute(source).GetCompletionValue().ToObject();
}

[Fact]
public void Empty()
{
Assert.Equal(Guid.Empty, RunTest($"Uuid.parse('{Guid.Empty}')"));
Assert.Equal(Guid.Empty, RunTest($"Uuid.Empty"));
}

[Fact]
public void Random()
{
var actual = RunTest($"new Uuid()");
Assert.NotEqual(Guid.Empty, actual);
Assert.IsType<Guid>(actual);
}

[Fact]
public void Copy()
{
var actual = (bool)RunTest($"const g = new Uuid(); copy(g).toString() === g.toString()");
Assert.True(actual);
}
}
}
17 changes: 12 additions & 5 deletions Jint/Native/Function/FunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal enum FunctionThisMode
JintFunctionDefinition function,
LexicalEnvironment scope,
FunctionThisMode thisMode)
: this(engine, !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name) : null, thisMode)
: this(engine, !string.IsNullOrWhiteSpace(function.Name) ? new JsString(function.Name) : null, thisMode)
{
_functionDefinition = function;
_environment = scope;
Expand All @@ -51,6 +51,13 @@ internal enum FunctionThisMode
_thisMode = thisMode;
}

protected FunctionInstance(
Engine engine,
JsString name)
: this(engine, name, FunctionThisMode.Global, ObjectClass.Function)
{
}

/// <summary>
/// Executed when a function object is used as a function
/// </summary>
Expand All @@ -60,7 +67,7 @@ internal enum FunctionThisMode
public abstract JsValue Call(JsValue thisObject, JsValue[] arguments);

public bool Strict => _thisMode == FunctionThisMode.Strict;

public virtual bool HasInstance(JsValue v)
{
if (!(v is ObjectInstance o))
Expand Down Expand Up @@ -212,7 +219,7 @@ internal void SetFunctionName(JsValue name, string prefix = null, bool force = f
{
return;
}

if (name is JsSymbol symbol)
{
name = symbol._value.IsUndefined()
Expand Down Expand Up @@ -258,7 +265,7 @@ public override string ToString()
{
name = TypeConverter.ToString(nameValue);
}
return "function " + name + "() {{[native code]}}";
return "function " + name + "() {{[native code]}}";
}
}
}
}

0 comments on commit 5d1114d

Please sign in to comment.