1 // 2 // Copyright (c) 2010-2025 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections.Generic; 10 using System.IO; 11 using System.Linq; 12 using System.Text; 13 using Antmicro.Migrant; 14 using Antmicro.Migrant.Hooks; 15 using AntShell.Commands; 16 using IronPython.Runtime; 17 using IronPython.Runtime.Operations; 18 using Microsoft.Scripting.Hosting; 19 using Antmicro.Renode.Core; 20 using Antmicro.Renode.Exceptions; 21 using Antmicro.Renode.UserInterface.Tokenizer; 22 using Antmicro.Renode.Utilities; 23 24 namespace Antmicro.Renode.UserInterface 25 { 26 public class MonitorPythonEngine : PythonEngine, IDisposable 27 { 28 private readonly string[] Imports = 29 { 30 "clr.AddReference('Infrastructure')", 31 }; 32 MonitorPythonEngine(Monitor monitor)33 public MonitorPythonEngine(Monitor monitor) 34 { 35 this.monitor = monitor; 36 var rootPath = Misc.GetRootDirectory(); 37 38 var imports = Engine.CreateScriptSourceFromString(Aggregate(Imports)); 39 imports.Execute(Scope); 40 41 var monitorPath = Path.Combine(rootPath, MonitorPyPath); 42 if(File.Exists(monitorPath)) 43 { 44 var script = Engine.CreateScriptSourceFromFile(monitorPath); // standard lib 45 script.Compile().Execute(Scope); 46 Logging.Logger.Log(Logging.LogLevel.Info, "Loaded monitor commands from: {0}", monitorPath); 47 } 48 49 Scope.SetVariable("self", monitor); 50 Scope.SetVariable("monitor", monitor); 51 } 52 Dispose()53 public void Dispose() 54 { 55 if(streamToEventConverter != null && streamToEventConverterForError != null) 56 { 57 streamToEventConverter.IgnoreWrites = true; 58 streamToEventConverterForError.IgnoreWrites = true; 59 } 60 } 61 62 [PreSerialization] BeforeSerialization()63 protected void BeforeSerialization() 64 { 65 throw new NotSupportedException("MonitorPythonEngine should not be serialized!"); 66 } 67 ExecuteBuiltinCommand(Token[] command, ICommandInteraction writer)68 public bool ExecuteBuiltinCommand(Token[] command, ICommandInteraction writer) 69 { 70 var command_name = ((LiteralToken)command[0]).Value; 71 if(!Scope.ContainsVariable("mc_" + command_name)) 72 { 73 return false; 74 } 75 76 object comm = Scope.GetVariable("mc_" + command_name); // get a method 77 var arguments = command.Skip(1); 78 var argumentsLength = command.Length - 1; 79 80 var firstEqualIndex = arguments.IndexOf(t => t is EqualityToken); 81 var parametersLength = firstEqualIndex == -1 ? argumentsLength : firstEqualIndex - 1; 82 83 if(arguments.Any() && arguments.First() is EqualityToken firstArgument) 84 { 85 throw new RecoverableException($"Invalid argument {firstArgument} at position 1"); 86 } 87 88 var parameters = arguments.Take(parametersLength).Select(GetTokenValue).ToArray(); 89 var keywordArguments = arguments.Skip(parametersLength).Split(3).Select((kwarg, idx) => 90 { 91 if(kwarg.Length != 3 || !(kwarg[0] is LiteralToken) || !(kwarg[1] is EqualityToken)) 92 { 93 var argument = string.Join<object>("", kwarg); 94 throw new RecoverableException($"Invalid keyword argument {argument} at position {parametersLength + 1 + idx}"); 95 } 96 97 var key = kwarg[0].GetObjectValue(); 98 var value = GetTokenValue(kwarg[2]); 99 return new KeyValuePair<object, object>(key, value); 100 }).ToDictionary(kv => kv.Key, kv => kv.Value); 101 102 ConfigureOutput(writer); 103 104 try 105 { 106 var result = PythonCalls.CallWithKeywordArgs(DefaultContext.Default, comm, parameters, keywordArguments); 107 if(result != null && (!(result is bool) || !(bool)result)) 108 { 109 writer.WriteError(String.Format("Command {0} failed, returning \"{1}\".", command_name, result)); 110 } 111 } 112 catch(Exception e) 113 { 114 throw new RecoverableException($"Command '{command_name} {String.Join(" ", parameters)}' failed", e); 115 } 116 return true; 117 } 118 TryExecutePythonScript(ReadFilePath fileName, ICommandInteraction writer)119 public bool TryExecutePythonScript(ReadFilePath fileName, ICommandInteraction writer) 120 { 121 var script = Engine.CreateScriptSourceFromFile(fileName); 122 ExecutePythonScriptInner(script, writer); 123 return true; 124 } 125 ExecutePythonCommand(string command, ICommandInteraction writer)126 public object ExecutePythonCommand(string command, ICommandInteraction writer) 127 { 128 try 129 { 130 var script = Engine.CreateScriptSourceFromString(command); 131 return ExecutePythonScriptInner(script, writer); 132 } 133 catch(Microsoft.Scripting.SyntaxErrorException e) 134 { 135 throw new RecoverableException(String.Format("Line : {0}\n{1}", e.Line, e.Message)); 136 } 137 } 138 GetTokenValue(Token token)139 private object GetTokenValue(Token token) 140 { 141 var value = token.GetObjectValue(); 142 if(token is LiteralToken) 143 { 144 if(EmulationManager.Instance.CurrentEmulation.TryGetEmulationElementByName(value as string, monitor.Machine, out var emulationElement)) 145 { 146 return emulationElement; 147 } 148 throw new RecoverableException($"No such emulation element: {value}"); 149 } 150 return value; 151 } 152 ExecutePythonScriptInner(ScriptSource script, ICommandInteraction writer)153 private object ExecutePythonScriptInner(ScriptSource script, ICommandInteraction writer) 154 { 155 ConfigureOutput(writer); 156 try 157 { 158 return script.Execute(Scope); 159 } 160 catch(Exception e) 161 { 162 throw new RecoverableException(e); 163 } 164 } 165 GetPythonCommands(string prefix = R, bool trimPrefix = true)166 public string[] GetPythonCommands(string prefix = "mc_", bool trimPrefix = true) 167 { 168 return Scope.GetVariableNames().Where(x => x.StartsWith(prefix ?? string.Empty, StringComparison.Ordinal)).Select(x => x.Substring(trimPrefix ? prefix.Length : 0)).ToArray(); 169 } 170 ConfigureOutput(ICommandInteraction writer)171 private void ConfigureOutput(ICommandInteraction writer) 172 { 173 streamToEventConverter = new StreamToEventConverter(); 174 streamToEventConverterForError = new StreamToEventConverter(); 175 var utf8WithoutBom = new UTF8Encoding(false); 176 177 var inputStream = writer.GetRawInputStream(); 178 if(inputStream != null) 179 { 180 Engine.Runtime.IO.SetInput(inputStream, utf8WithoutBom); 181 } 182 Engine.Runtime.IO.SetOutput(streamToEventConverter, utf8WithoutBom); 183 Engine.Runtime.IO.SetErrorOutput(streamToEventConverterForError, utf8WithoutBom); 184 streamToEventConverter.BytesWritten += bytes => writer.Write(utf8WithoutBom.GetString(bytes).Replace("\n", "\r\n")); 185 streamToEventConverterForError.BytesWritten += bytes => writer.WriteError(utf8WithoutBom.GetString(bytes).Replace("\n", "\r\n")); 186 } 187 188 private StreamToEventConverter streamToEventConverter; 189 private StreamToEventConverter streamToEventConverterForError; 190 private readonly Monitor monitor; 191 private const string MonitorPyPath = "scripts/monitor.py"; 192 } 193 } 194