diff --git a/new/AutoItInterpreter/AssemblyInfo.cs b/new/AutoItInterpreter/AssemblyInfo.cs index bddc8f55..b7679ac7 100644 --- a/new/AutoItInterpreter/AssemblyInfo.cs +++ b/new/AutoItInterpreter/AssemblyInfo.cs @@ -1,15 +1,15 @@ ////////////////////////////////////////////////////////////////////////// -// Auto-generated 2020-07-26 17:04:48.009 // +// Auto-generated 2020-07-27 01:57:30.012 // // ANY CHANGES TO THIS DOCUMENT WILL BE LOST UPON RE-GENERATION // ////////////////////////////////////////////////////////////////////////// using System.Reflection; using System; -[assembly: AssemblyVersion("0.6.1256.7328")] -[assembly: AssemblyFileVersion("0.6.1256.7328")] -[assembly: AssemblyInformationalVersion("cdfd24e8c9341f9fc41540bef89d37e540605174")] +[assembly: AssemblyVersion("0.6.1257.7329")] +[assembly: AssemblyFileVersion("0.6.1257.7329")] +[assembly: AssemblyInformationalVersion("f3c8f7753e53011c5fac5224b49c3724c68f2759")] [assembly: AssemblyCompany("Unknown6656")] [assembly: AssemblyCopyright("Copyright © 2018 - 2020, Unknown6656")] [assembly: AssemblyProduct("AutoIt3 Interpreter by Unknown6656")] @@ -35,11 +35,11 @@ public static class __module__ /// /// The interpreter's current version. /// - public static Version? InterpreterVersion { get; } = Version.Parse("0.6.1256.7328"); + public static Version? InterpreterVersion { get; } = Version.Parse("0.6.1257.7329"); /// /// The Git hash associated with the current build. /// - public const string GitHash = "cdfd24e8c9341f9fc41540bef89d37e540605174"; + public const string GitHash = "f3c8f7753e53011c5fac5224b49c3724c68f2759"; /// /// The URL of this project's Git(Hub) repository. /// diff --git a/new/AutoItInterpreter/MainProgram.cs b/new/AutoItInterpreter/MainProgram.cs index a5d7b072..6819b100 100644 --- a/new/AutoItInterpreter/MainProgram.cs +++ b/new/AutoItInterpreter/MainProgram.cs @@ -135,7 +135,7 @@ public static int Start(string[] argv) Console.CancelKeyPress += (_, e) => { - Interpreter[] instances = Interpreter.Instances; + Interpreter[] instances = Interpreter.ActiveInstances; e.Cancel = instances.Length > 0; diff --git a/new/AutoItInterpreter/Runtime/AU3Thread.cs b/new/AutoItInterpreter/Runtime/AU3Thread.cs index 1a67c6bd..d09a1145 100644 --- a/new/AutoItInterpreter/Runtime/AU3Thread.cs +++ b/new/AutoItInterpreter/Runtime/AU3Thread.cs @@ -1,24 +1,14 @@ -using System.Text.RegularExpressions; -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Linq; using System; -using Microsoft.FSharp.Collections; -using Microsoft.FSharp.Core; - -using Piglet.Parser.Configuration.Generic; - -using Unknown6656.AutoIt3.Extensibility.Plugins.Au3Framework; -using Unknown6656.AutoIt3.Extensibility.Plugins.Internals; -using Unknown6656.AutoIt3.ExpressionParser; -using Unknown6656.AutoIt3.Extensibility; using Unknown6656.Common; -using static Unknown6656.AutoIt3.ExpressionParser.AST; - namespace Unknown6656.AutoIt3.Runtime { + /// + /// Represents an AutoIt execution thread. The thread consists of multiple call frames which in term represent function invocations. + /// public sealed class AU3Thread : IDisposable , IEquatable @@ -29,24 +19,51 @@ public sealed class AU3Thread private int? _override_exitcode = null; + /// + /// The on which the current thread has been created. + /// public Interpreter Interpreter { get; } + /// + /// Indicates whether the current thread is actively running. + /// public bool IsRunning => _running; + /// + /// The top-most , representing the top-most function invocation of this thread. + /// public CallFrame? CurrentFrame => _callstack.TryPeek(out CallFrame? lp) ? lp : null; + /// + /// The of the currently executed statement or line. + /// public SourceLocation? CurrentLocation => CallStack.OfType().FirstOrDefault()?.CurrentLocation ?? SourceLocation.Unknown; + /// + /// The currently executed function. + /// public ScriptFunction? CurrentFunction => CurrentFrame?.CurrentFunction; public VariableScope CurrentVariableResolver => CurrentFrame?.VariableResolver ?? Interpreter.VariableResolver; + /// + /// The current call stack (function invocation stack) of this thread. The first item of the collection represents the top-most thread and holds the same value as . + /// public CallFrame[] CallStack => CurrentFrame.Propagate(frame => (frame?.CallerFrame, frame is { })).ToArrayWhere(frame => frame is { })!; + /// + /// Indicates whether the current thread has been disposed. + /// public bool IsDisposed { get; private set; } + /// + /// Indicates whether the current thread is the of the associated . + /// public bool IsMainThread => ReferenceEquals(this, Interpreter.MainThread); + /// + /// The unique ID of this thread. + /// public int ThreadID { get; } @@ -59,6 +76,14 @@ internal AU3Thread(Interpreter interpreter) MainProgram.PrintfDebugMessage("debug.au3thread.created", this); } + /// + /// Starts the current thread by invoking the given with the given arguments. + /// + /// This function is blocking and returns only after the given function has been invoked. + /// + /// The function to be invoked. + /// The arguments to be passed to the function. + /// The functions return value or execution error. public Union Start(ScriptFunction function, Variant[] args) => Interpreter.Telemetry.Measure>(TelemetryCategory.ThreadRun, delegate { @@ -77,6 +102,16 @@ public Union Start(ScriptFunction function, Variant[] return result; }); + /// + /// [UNSAFE!] + /// Invokes the given with the given arguments. A call to this function is considered to be unsafe, as any non-concurrent call may result into undefined behavior. + /// Use instead. + /// + /// This function is blocking and returns only after the given function has been invoked. + /// + /// The function to be invoked. + /// The arguments to be passed to the function. + /// The functions return value or execution error. public Union Call(ScriptFunction function, Variant[] args) { if (IsDisposed) @@ -100,6 +135,9 @@ public Union Call(ScriptFunction function, Variant[] return result; } + /// + /// Stops the current thread execution. This will dispose the current thread. + /// public void Stop() { _running = false; @@ -107,6 +145,10 @@ public void Stop() Dispose(); } + /// + /// Stops the current thread execution with the given exit code. This will dispose the current thread. + /// + /// Exit code, with which the current thread will return. public void Stop(int exitcode) { Stop(); @@ -126,14 +168,19 @@ public void Stop(int exitcode) return CurrentLocation; } + /// public override string ToString() => $"0x{ThreadID:x4}{(IsMainThread ? " (main)" : "")} @ {CurrentLocation}"; + /// public override int GetHashCode() => HashCode.Combine(Interpreter, ThreadID); + /// public override bool Equals(object? obj) => obj is AU3Thread thread && Equals(thread); + /// public bool Equals(AU3Thread? other) => Interpreter == other?.Interpreter && ThreadID == other?.ThreadID; + /// public void Dispose() { _running = false; @@ -151,1562 +198,4 @@ public void Dispose() MainProgram.PrintfDebugMessage("debug.au3thread.disposed", this); } } - -#pragma warning disable CA1063 - // TODO : covariant return for 'CurrentFunction' - public abstract class CallFrame - : IDisposable - { - public AU3Thread CurrentThread { get; } - - public virtual ScriptFunction CurrentFunction { get; } - - public VariableScope VariableResolver { get; } - - public Variant[] PassedArguments { get; } - - public CallFrame? CallerFrame { get; } - - public Variant ReturnValue { protected set; get; } = Variant.Zero; - - public virtual SourceLocation CurrentLocation => CurrentThread.CurrentLocation ?? CurrentFunction.Location; - - public Interpreter Interpreter => CurrentThread.Interpreter; - - - internal CallFrame(AU3Thread thread, CallFrame? caller, ScriptFunction function, Variant[] args) - { - CurrentThread = thread; - CallerFrame = caller; - CurrentFunction = function; - PassedArguments = args; - - // TODO : the following line is wrong - it should be interpreter as parent, not the previous frame - VariableResolver = function.IsMainFunction ? thread.CurrentVariableResolver : thread.CurrentVariableResolver.CreateChildScope(this); - } - - public void Dispose() => VariableResolver.Dispose(); - - protected abstract Union InternalExec(Variant[] args); - - internal Union Execute(Variant[] args) - { - Union result = Variant.Zero; - ScannedScript script = CurrentFunction.Script; - - SetError(0, 0); - - Interpreter.Telemetry.Measure(TelemetryCategory.OnAutoItStart, delegate - { - if (CurrentFunction.IsMainFunction && script.LoadScript(this) is InterpreterError load_error) - result = load_error; - }); - - (int min_argc, int max_argc) = CurrentFunction.ParameterCount; - - if (args.Length < min_argc) - return InterpreterError.WellKnown(CurrentThread.CurrentLocation, "error.not_enough_args", CurrentFunction.Name, min_argc, args.Length); - else if (args.Length > max_argc) - return InterpreterError.WellKnown(CurrentThread.CurrentLocation, "error.too_many_args", CurrentFunction.Name, max_argc, args.Length); - else if (result.Is()) - if (CurrentThread.IsRunning) - { - MainProgram.PrintfDebugMessage("debug.au3thread.executing", CurrentFunction); - - result = Interpreter.Telemetry.Measure(TelemetryCategory.ScriptExecution, () => InternalExec(args)); - } - - Interpreter.Telemetry.Measure(TelemetryCategory.OnAutoItExit, delegate - { - if (CurrentFunction.IsMainFunction && result.Is() && script.UnLoadScript(this) is InterpreterError unload_error) - result = unload_error; - }); - - if (result.Is(out Variant @return)) - ReturnValue = @return; - - return result; - } - - public Union Call(ScriptFunction function, Variant[] args) => CurrentThread.Call(function, args); - - public Variant SetError(int error, Variant? extended = null, in Variant @return = default) - { - Interpreter.ErrorCode = error; - - return SetExtended(extended, in @return); - } - - public Variant SetExtended(Variant? extended, in Variant @return = default) - { - Interpreter.ExtendedValue = extended ?? Variant.Null; - - return @return; - } - - public void Print(Variant value) => Interpreter.Print(this, value); - - public void Print(object? value) => Interpreter.Print(this, value); - - public override string ToString() => $"[0x{CurrentThread.ThreadID:x4}]"; - - internal void IssueWarning(string key, params object?[] args) => MainProgram.PrintWarning(CurrentLocation, Interpreter.CurrentUILanguage[key, args]); - } -#pragma warning restore CA1063 - - public sealed class NativeCallFrame - : CallFrame - { - internal NativeCallFrame(AU3Thread thread, CallFrame? caller, NativeFunction function, Variant[] args) - : base(thread, caller, function, args) - { - } - - protected override Union InternalExec(Variant[] args) - { - FunctionReturnValue result = Interpreter.Telemetry.Measure(TelemetryCategory.NativeScriptExecution, () => ((NativeFunction)CurrentFunction).Execute(this, args)); - Variant? extended = null; - int error = 0; - - if (result.IsFatal(out InterpreterError? fatal)) - return fatal; - else if (result.IsSuccess(out Variant variant, out extended) || result.IsError(out variant, out error, out extended)) - return SetError(error, extended, in variant); - else - throw new InvalidOperationException("Return value could not be processed"); - } - - public override string ToString() => $"{base.ToString()} native call frame"; - } - - public sealed class AU3CallFrame - : CallFrame - { - private const RegexOptions _REGEX_OPTIONS = RegexOptions.IgnoreCase | RegexOptions.Compiled; - private static readonly Regex REGEX_INTERNAL_LABEL = new Regex(@"^§\w+$", _REGEX_OPTIONS); - private static readonly Regex REGEX_VARIABLE = new Regex(@"\$([^\W\d]|[^\W\d]\w*)\b", _REGEX_OPTIONS); - private static readonly Regex REGEX_GOTO = new Regex(@"^goto\s+(?