/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Scripting/Core/ScriptState.cs
281 строка
12 KB
Cyrus Najmabadi
Sort usings
19 июн 2025, 03:07
19 июн 2025, 03:07
b2899f6
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Scripting { /// <summary> /// The result of running a script. /// </summary> public abstract class ScriptState { /// <summary> /// The script that ran to produce this result. /// </summary> public Script Script { get; } /// <summary> /// Caught exception originating from the script top-level code. /// </summary> /// <remarks> /// Exceptions are only caught and stored here if the API returning the <see cref="ScriptState"/> is instructed to do so. /// By default they are propagated to the caller of the API. /// </remarks> public Exception Exception { get; } internal ScriptExecutionState ExecutionState { get; } private ImmutableArray<ScriptVariable> _lazyVariables; private IReadOnlyDictionary<string, int> _lazyVariableMap; internal ScriptState(ScriptExecutionState executionState, Script script, Exception exceptionOpt) { Debug.Assert(executionState != null); Debug.Assert(script != null); ExecutionState = executionState; Script = script; Exception = exceptionOpt; } /// <summary> /// The final value produced by running the script. /// </summary> public object ReturnValue => GetReturnValue(); internal abstract object GetReturnValue(); /// <summary> /// Returns variables defined by the scripts in the declaration order. /// </summary> public ImmutableArray<ScriptVariable> Variables { get { if (_lazyVariables == null) { ImmutableInterlocked.InterlockedInitialize(ref _lazyVariables, CreateVariables()); } return _lazyVariables; } } /// <summary> /// Returns a script variable of the specified name. /// </summary> /// <remarks> /// If multiple script variables are defined in the script (in distinct submissions) returns the last one. /// Name lookup is case sensitive in C# scripts and case insensitive in VB scripts. /// </remarks> /// <returns><see cref="ScriptVariable"/> or null, if no variable of the specified <paramref name="name"/> is defined in the script.</returns> /// <exception cref="ArgumentNullException"><paramref name="name"/> is null.</exception> public ScriptVariable GetVariable(string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } int index; return GetVariableMap().TryGetValue(name, out index) ? Variables[index] : null; } private ImmutableArray<ScriptVariable> CreateVariables() { var result = ArrayBuilder<ScriptVariable>.GetInstance(); var executionState = ExecutionState; // Don't include the globals object (slot #0) for (int i = 1; i < executionState.SubmissionStateCount; i++) { var state = executionState.GetSubmissionState(i); Debug.Assert(state != null); foreach (var field in state.GetType().GetTypeInfo().DeclaredFields) { // TODO: synthesized fields of submissions shouldn't be public if (field.IsPublic && field.Name.Length > 0 && (char.IsLetterOrDigit(field.Name[0]) || field.Name[0] == '_')) { result.Add(new ScriptVariable(state, field)); } } } return result.ToImmutableAndFree(); } private IReadOnlyDictionary<string, int> GetVariableMap() { if (_lazyVariableMap == null) { var map = new Dictionary<string, int>(Script.Compiler.IdentifierComparer); for (int i = 0; i < Variables.Length; i++) { map[Variables[i].Name] = i; } _lazyVariableMap = map; } return _lazyVariableMap; } /// <summary> /// Continues script execution from the state represented by this instance by running the specified code snippet. /// </summary> /// <param name="code">The code to be executed.</param> /// <param name="options">Options.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables and return value.</returns> public Task<ScriptState<object>> ContinueWithAsync(string code, ScriptOptions options, CancellationToken cancellationToken) => ContinueWithAsync<object>(code, options, null, cancellationToken); /// <summary> /// Continues script execution from the state represented by this instance by running the specified code snippet. /// </summary> /// <param name="code">The code to be executed.</param> /// <param name="options">Options.</param> /// <param name="catchException"> /// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>. /// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables, return value and caught exception (if applicable).</returns> public Task<ScriptState<object>> ContinueWithAsync(string code, ScriptOptions options = null, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken)) => Script.ContinueWith<object>(code, options).RunFromAsync(this, catchException, cancellationToken); /// <summary> /// Continues script execution from the state represented by this instance by running the specified code snippet. /// </summary> /// <param name="code">The code to be executed.</param> /// <param name="options">Options.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables and return value.</returns> public Task<ScriptState<TResult>> ContinueWithAsync<TResult>(string code, ScriptOptions options, CancellationToken cancellationToken) => ContinueWithAsync<TResult>(code, options, null, cancellationToken); /// <summary> /// Continues script execution from the state represented by this instance by running the specified code snippet. /// </summary> /// <param name="code">The code to be executed.</param> /// <param name="options">Options.</param> /// <param name="catchException"> /// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>. /// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller. /// </param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables, return value and caught exception (if applicable).</returns> public Task<ScriptState<TResult>> ContinueWithAsync<TResult>(string code, ScriptOptions options = null, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken)) => Script.ContinueWith<TResult>(code, options).RunFromAsync(this, catchException, cancellationToken); // How do we resolve overloads? We should use the language semantics. // https://github.com/dotnet/roslyn/issues/3720 #if TODO /// <summary> /// Invoke a method declared by the script. /// </summary> public object Invoke(string name, params object[] args) { var func = this.FindMethod(name, args != null ? args.Length : 0); if (func != null) { return func(args); } return null; } private Func<object[], object> FindMethod(string name, int argCount) { for (int i = _executionState.Count - 1; i >= 0; i--) { var sub = _executionState[i]; if (sub != null) { var type = sub.GetType(); var method = FindMethod(type, name, argCount); if (method != null) { return (args) => method.Invoke(sub, args); } } } return null; } private MethodInfo FindMethod(Type type, string name, int argCount) { return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } /// <summary> /// Create a delegate to a method declared by the script. /// </summary> public TDelegate CreateDelegate<TDelegate>(string name) { var delegateInvokeMethod = typeof(TDelegate).GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public); for (int i = _executionState.Count - 1; i >= 0; i--) { var sub = _executionState[i]; if (sub != null) { var type = sub.GetType(); var method = FindMatchingMethod(type, name, delegateInvokeMethod); if (method != null) { return (TDelegate)(object)method.CreateDelegate(typeof(TDelegate), sub); } } } return default(TDelegate); } private MethodInfo FindMatchingMethod(Type instanceType, string name, MethodInfo delegateInvokeMethod) { var dprms = delegateInvokeMethod.GetParameters(); foreach (var mi in instanceType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { if (mi.Name == name) { var prms = mi.GetParameters(); if (prms.Length == dprms.Length) { // TODO: better matching.. return mi; } } } return null; } #endif } public sealed class ScriptState<T> : ScriptState { public new T ReturnValue { get; } internal override object GetReturnValue() => ReturnValue; internal ScriptState(ScriptExecutionState executionState, Script script, T value, Exception exceptionOpt) : base(executionState, script, exceptionOpt) { ReturnValue = value; } } }