L'hôte (IScriptHost)
Le moteur est volontairement agnostique de son environnement d'exécution : tout ce qui touche au
monde extérieur — système de fichiers, flux standard, horloge, variables d'environnement, exécution
de commandes — passe par YScript.Hosting.IScriptHost.
ScriptEngine/Runtime.ScriptState appelle cette interface, jamais l'API .NET directement, pour
toute opération de ce type demandée par un script (io.*, os.*, require d'un chemin, ...).
Un hôte se fournit au constructeur :
using var engine = new ScriptEngine(myHost, options);
Si aucun hôte n'est fourni, YScript.Hosting.DefaultHost est utilisé. Il est pensé pour un usage
embarqué/sandboxé plutôt que pour un outil en ligne de commande :
StandardOutput/StandardErrorécrivent dans desStringBuilderen mémoire (Output/ErrorOutput), pas dans la console du process — unprint()de script n'apparaît nulle part tant que vous ne lisez pas ces buffers.StandardInputlitConsole.In.HasCommandShellvautfalse:os.execute/io.popensont refusés (NotSupportedException). Système de fichiers, horloge (Now), et temps CPU (ProcessCpuTime) délèguent en revanche à l'implémentation .NET standard (System.IO,DateTimeOffset.Now,Process).HasProcessCultureControlvautfalse:os.setculture(nom, "process")est refusé (lecture viaProcessCulturetoujours possible, seule la mutation est gardée) — même logique queHasCommandShell, la culture du processus étant visible en dehors du moteur.
L'interpréteur en ligne de commande (voir L'interpréteur) illustre l'écart type
entre un hôte sandboxé et un hôte "plein accès" : son hôte ConsoleHost hérite de DefaultHost et
ne redéfinit que ce qui doit se
comporter comme un vrai terminal — stdout/stderr câblés sur Console.Out/Console.Error,
HasCommandShell => true avec ExecuteCommand/OpenProcessPipe lançant réellement un process shell
(cmd.exe//bin/sh), HasProcessCultureControl => true (un vrai terminal laisse l'utilisateur
changer sa propre locale), et un nom d'encodage "oem" supplémentaire propre à Windows.
Ce qu'un hôte personnalisé implémente
IScriptHost se découpe en quatre familles, chaque méthode documentant dans son commentaire XML par
quelle fonction de bibliothèque standard elle est appelée :
- Environnement —
SharePath/UserPath/ExecutablePath/GetEnv: emplacements de fichiers et variables d'environnement. - Sortie texte —
WriteString/WriteLineString, séparées en canalStandard/Error(HostOutput) : ce queprint()et les messages d'erreur/avertissement du moteur écrivent au final. - Fichiers (nécessaire pour
OpenIOLib) — ouverture/lecture/écriture de fichiers, test d'existence, énumération de répertoire,StandardInput/StandardOutput/StandardErrorcomme poignées de fichier (IFileHandle), résolution d'encodages nommés (GetEncodingByName) ou par codepage. - Process/OS (nécessaire pour
OpenOSLib) — horloge (Now), temps CPU (ProcessCpuTime), suppression/renommage de fichier, et exécution de commande shell (ExecuteCommand/OpenProcessPipe), gardée derrièreHasCommandShell: un hôte qui ne veut pas laisser un script lancer des processus arbitraires laisse cette propriété àfalse(comportement deDefaultHost) —ExecuteCommand/OpenProcessPipene sont alors jamais appelées par la bibliothèqueos/io. Même logique pourSetProcessCulture(os.setculture(nom, "process")), gardée derrièreHasProcessCultureControl— la culture propre au moteur (IScript.Culture,os.setculture(nom)sans portée"process") n'a en revanche pas besoin d'un tel garde-fou, puisqu'elle n'affecte que ce moteur.
La façon la plus simple de personnaliser un hôte est d'hériter de DefaultHost et de ne redéfinir
(virtual) que ce qui diffère — comme le fait ConsoleHost — plutôt que de réimplémenter
IScriptHost intégralement.
Exemple : rediriger la sortie standard vers un composant applicatif
public class UiHost : DefaultHost
{
readonly Action<string> _onOutput;
IFileHandle _stdout;
public UiHost(Action<string> onOutput) => _onOutput = onOutput;
public override IFileHandle StandardOutput =>
_stdout ??= new TextIOFileHandle("stdout", FileAccessMode.Write,
writer: new ActionTextWriter(_onOutput));
}
(ActionTextWriter serait un petit TextWriter maison qui relaie chaque écriture à _onOutput —
non fourni par le moteur.)
Exemple : terminer seulement le script, pas le process
DefaultHost.Exit appelle Environment.Exit — adapté à un outil CLI, pas à un moteur embarqué dans
une application plus large où os.exit() ne doit arrêter que le script en cours :
public override void Exit(int exitCode)
=> throw new ScriptExitRequestedException(exitCode); // à capturer autour de l'exécution du script