Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions Jint.Repl/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Jint.Native.Json;
using Jint.Runtime;

// ReSharper disable LocalizableElement

#pragma warning disable IL2026
#pragma warning disable IL2111

Expand All @@ -13,6 +15,7 @@

engine
.SetValue("print", new Action<object>(Console.WriteLine))
.SetValue("console", new JsConsole())
.SetValue("load", new Func<string, object>(
path => engine.Evaluate(File.ReadAllText(path)))
);
Expand All @@ -22,7 +25,7 @@
{
if (!File.Exists(filename))
{
Console.WriteLine("Could not find file: {0}", filename);
Console.WriteLine($"Could not find file: {filename}");
}

var script = File.ReadAllText(filename);
Expand All @@ -33,10 +36,8 @@
var assembly = Assembly.GetExecutingAssembly();
var version = assembly.GetName().Version?.ToString();

Console.WriteLine("Welcome to Jint ({0})", version);
Console.WriteLine("Type 'exit' to leave, " +
"'print()' to write on the console, " +
"'load()' to load scripts.");
Console.WriteLine($"Welcome to Jint ({version})");
Console.WriteLine("Type 'exit' to leave, 'print()' to write on the console, 'load()' to load scripts.");
Console.WriteLine();

var defaultColor = Console.ForegroundColor;
Expand Down Expand Up @@ -86,3 +87,18 @@
Console.WriteLine(e.Message);
}
}

file sealed class JsConsole
{
public void Log(object value)
{
Console.WriteLine(value?.ToString() ?? "null");
}

public void Error(object value)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(value?.ToString() ?? "null");
Console.ResetColor();
}
}
4 changes: 1 addition & 3 deletions Jint.Tests/Runtime/TypedArrayInteropTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using Jint.Native;

namespace Jint.Tests.Runtime;

public class TypedArrayInteropTests
Expand Down Expand Up @@ -136,7 +134,7 @@ public void CanInteropWithFloat16()
Assert.True(fromEngine.IsFloat16Array());
Assert.Equal(source, fromEngine.AsFloat16Array());

engine.SetValue("testFunc", new Func<JsTypedArray, JsTypedArray>(v => v));
engine.SetValue("testFunc", new Func<Native.JsTypedArray, Native.JsTypedArray>(v => v));
Assert.Equal(source, engine.Evaluate("testFunc(testSubject)").AsFloat16Array());
}
#endif
Expand Down
34 changes: 34 additions & 0 deletions Jint/Native/Disposable/AsyncDisposableStackConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;

namespace Jint.Native.Disposable;

internal sealed class AsyncDisposableStackConstructor : Constructor
{
private static readonly JsString _name = new("AsyncDisposableStack");

public AsyncDisposableStackConstructor(Engine engine, Realm realm) : base(engine, realm, _name)
{
PrototypeObject = new AsyncDisposableStackPrototype(engine, realm, this, engine.Intrinsics.Object.PrototypeObject);
_length = new PropertyDescriptor(0, PropertyFlag.Configurable);
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}

internal AsyncDisposableStackPrototype PrototypeObject { get; }

public override ObjectInstance Construct(JsCallArguments arguments, JsValue newTarget)
{
if (newTarget.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_realm);
}

var stack = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.AsyncDisposableStack.PrototypeObject,
static (Engine engine, Realm _, object? _) => new DisposableStack(engine, DisposeHint.Async));

return stack;
}
}
101 changes: 101 additions & 0 deletions Jint/Native/Disposable/AsyncDisposableStackPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using Jint.Native.Object;
using Jint.Native.Symbol;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;

namespace Jint.Native.Disposable;

internal sealed class AsyncDisposableStackPrototype : Prototype
{
private readonly AsyncDisposableStackConstructor _constructor;

internal AsyncDisposableStackPrototype(
Engine engine,
Realm realm,
AsyncDisposableStackConstructor constructor,
ObjectPrototype objectPrototype) : base(engine, realm)
{
_prototype = objectPrototype;
_constructor = constructor;
}

protected override void Initialize()
{
var disposeFunction = new ClrFunction(Engine, "disposeAsync", Dispose, 0, PropertyFlag.Configurable);

const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
var properties = new PropertyDictionary(8, checkExistingKeys: false)
{
["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
["adopt"] = new PropertyDescriptor(new ClrFunction(Engine, "adopt", Adopt, 2, PropertyFlag.Configurable), PropertyFlags),
["defer"] = new PropertyDescriptor(new ClrFunction(Engine, "defer", Defer, 1, PropertyFlag.Configurable), PropertyFlags),
["disposeAsync"] = new PropertyDescriptor(disposeFunction, PropertyFlags),
["disposed"] = new GetSetPropertyDescriptor(get: new ClrFunction(Engine, "get disposed", Disposed, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlags),
["move"] = new PropertyDescriptor(new ClrFunction(Engine, "move", Move, 0, PropertyFlag.Configurable), PropertyFlags),
["use"] = new PropertyDescriptor(new ClrFunction(Engine, "use", Use, 1, PropertyFlag.Configurable), PropertyFlags),
};
SetProperties(properties);

var symbols = new SymbolDictionary(2)
{
[GlobalSymbolRegistry.AsyncDispose] = new PropertyDescriptor(disposeFunction, PropertyFlags),
[GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("AsyncDisposableStack", PropertyFlag.Configurable),
};
SetSymbols(symbols);
}

private JsValue Adopt(JsValue thisObject, JsCallArguments arguments)
{
var stack = AssertDisposableStack(thisObject);
return stack.Adopt(arguments.At(0), arguments.At(1));
}

private JsValue Defer(JsValue thisObject, JsCallArguments arguments)
{
var stack = AssertDisposableStack(thisObject);
stack.Defer(arguments.At(0));
return Undefined;
}

private JsValue Dispose(JsValue thisObject, JsCallArguments arguments)
{
var stack = AssertDisposableStack(thisObject);
return stack.Dispose();
}

private JsValue Disposed(JsValue thisObject, JsCallArguments arguments)
{
var stack = AssertDisposableStack(thisObject);
return stack.State == DisposableState.Disposed;
}

private JsValue Move(JsValue thisObject, JsCallArguments arguments)
{
var stack = AssertDisposableStack(thisObject);
var newDisposableStack = _engine.Intrinsics.Function.OrdinaryCreateFromConstructor(
_engine.Intrinsics.AsyncDisposableStack,
static intrinsics => intrinsics.AsyncDisposableStack.PrototypeObject,
static (Engine engine, Realm _, object? _) => new DisposableStack(engine, DisposeHint.Async));

return stack.Move(newDisposableStack);
}

private JsValue Use(JsValue thisObject, JsCallArguments arguments)
{
var stack = AssertDisposableStack(thisObject);
return stack.Use(arguments.At(0));
}

private DisposableStack AssertDisposableStack(JsValue thisObject)
{
if (thisObject is not DisposableStack { _hint: DisposeHint.Async } stack)
{
ExceptionHelper.ThrowTypeError(_engine.Realm, "This is not a AsyncDisposableStack instance.");
return null!;
}

return stack;
}
}
95 changes: 95 additions & 0 deletions Jint/Native/Disposable/DisposableStack.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Interop;

namespace Jint.Native.Disposable;

internal enum DisposableState
{
Pending,
Disposed,
}

internal sealed class DisposableStack : ObjectInstance
{
internal readonly DisposeHint _hint;
private DisposeCapability _disposeCapability;

public DisposableStack(Engine engine, DisposeHint hint) : base(engine)
{
_hint = hint;
State = DisposableState.Pending;
_disposeCapability = new DisposeCapability(engine);
}

public DisposableState State { get; private set; }

public JsValue Dispose()
{
if (State == DisposableState.Disposed)
{
return Undefined;
}

State = DisposableState.Disposed;
var completion = _disposeCapability.DisposeResources(new Completion(CompletionType.Normal, Undefined, _engine.GetLastSyntaxElement()));
if (completion.Type == CompletionType.Throw)
{
ExceptionHelper.ThrowJavaScriptException(_engine, completion.Value, completion);
}
return completion.Value;
}

public void Defer(JsValue onDispose)
{
AddDisposableResource(Undefined, _hint, onDispose.GetCallable(_engine.Realm));
}

public JsValue Use(JsValue value)
{
AddDisposableResource(value, _hint);
return value;
}

public JsValue Adopt(JsValue value, JsValue onDispose)
{
AssertNotDisposed();

var callable = onDispose.GetCallable(_engine.Realm);
JsCallDelegate closure = (_, _) =>
{
callable.Call(Undefined, value);
return Undefined;
};

var f = new ClrFunction(_engine, string.Empty, closure);
AddDisposableResource(Undefined, DisposeHint.Sync, f);

return value;
}

public JsValue Move(DisposableStack newDisposableStack)
{
AssertNotDisposed();

newDisposableStack.State = DisposableState.Pending;
newDisposableStack._disposeCapability = this._disposeCapability;
this._disposeCapability = new DisposeCapability(_engine);
State = DisposableState.Disposed;
return newDisposableStack;
}

private void AddDisposableResource(JsValue v, DisposeHint hint, ICallable? method = null)
{
AssertNotDisposed();
_disposeCapability.AddDisposableResource(v, hint, method);
}

private void AssertNotDisposed()
{
if (State == DisposableState.Disposed)
{
ExceptionHelper.ThrowReferenceError(_engine.Realm, "Stack already disposed.");
}
}
}
34 changes: 34 additions & 0 deletions Jint/Native/Disposable/DisposableStackConstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;

namespace Jint.Native.Disposable;

internal sealed class DisposableStackConstructor : Constructor
{
private static readonly JsString _name = new("DisposableStack");

public DisposableStackConstructor(Engine engine, Realm realm) : base(engine, realm, _name)
{
PrototypeObject = new DisposableStackPrototype(engine, realm, this, engine.Intrinsics.Object.PrototypeObject);
_length = new PropertyDescriptor(0, PropertyFlag.Configurable);
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}

internal DisposableStackPrototype PrototypeObject { get; }

public override ObjectInstance Construct(JsCallArguments arguments, JsValue newTarget)
{
if (newTarget.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_realm);
}

var stack = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.DisposableStack.PrototypeObject,
static (Engine engine, Realm _, object? _) => new DisposableStack(engine, DisposeHint.Sync));

return stack;
}
}
Loading