BizHawk/BizHawk.Client.Common/lua/LuaSandbox.cs

104 lines
3.2 KiB
C#

using System;
using System.Runtime.CompilerServices;
using BizHawk.Common;
using NLua;
// TODO - evaluate for re-entrancy problems
namespace BizHawk.Client.Common
{
public class LuaSandbox
{
private static readonly ConditionalWeakTable<Lua, LuaSandbox> SandboxForThread = new ConditionalWeakTable<Lua, LuaSandbox>();
public static Action<string> DefaultLogger { get; set; }
public void SetSandboxCurrentDirectory(string dir)
{
_currentDirectory = dir;
}
private string _currentDirectory;
private bool CoolSetCurrentDirectory(string path, string currDirSpeedHack = null)
{
string target = $"{_currentDirectory}\\";
// first we'll bypass it with a general hack: don't do any setting if the value's already there (even at the OS level, setting the directory can be slow)
// yeah I know, not the smoothest move to compare strings here, in case path normalization is happening at some point
// but you got any better ideas?
currDirSpeedHack ??= OSTailoredCode.IsUnixHost ? Environment.CurrentDirectory : CWDHacks.Get();
if (currDirSpeedHack == path) return true;
if (!OSTailoredCode.IsUnixHost) return CWDHacks.Set(target);
if (!System.IO.Directory.Exists(_currentDirectory)) return false; //TODO is this necessary with Mono? Linux is fine with the CWD being nonexistent. also, is this necessary with .NET Core on Windows? --yoshi
Environment.CurrentDirectory = _currentDirectory;
return true;
}
private void Sandbox(Action callback, Action exceptionCallback)
{
string savedEnvironmentCurrDir = null;
try
{
savedEnvironmentCurrDir = Environment.CurrentDirectory;
if (_currentDirectory != null)
{
CoolSetCurrentDirectory(_currentDirectory, savedEnvironmentCurrDir);
}
EnvironmentSandbox.Sandbox(callback);
}
catch (NLua.Exceptions.LuaException ex)
{
Console.WriteLine(ex);
DefaultLogger(ex.ToString());
exceptionCallback?.Invoke();
}
finally
{
if (_currentDirectory != null)
{
CoolSetCurrentDirectory(savedEnvironmentCurrDir);
}
}
}
public static LuaSandbox CreateSandbox(Lua thread, string initialDirectory)
{
var sandbox = new LuaSandbox();
SandboxForThread.Add(thread, sandbox);
sandbox.SetSandboxCurrentDirectory(initialDirectory);
return sandbox;
}
/// <exception cref="InvalidOperationException">could not get sandbox reference for thread (<see cref="CreateSandbox"/> has not been called)</exception>
public static LuaSandbox GetSandbox(Lua thread)
{
// this is just placeholder.
// we shouldn't be calling a sandbox with no thread--construct a sandbox properly
if (thread == null)
{
return new LuaSandbox();
}
lock (SandboxForThread)
{
if (SandboxForThread.TryGetValue(thread, out var sandbox))
{
return sandbox;
}
// for now: throw exception (I want to manually creating them)
// return CreateSandbox(thread);
throw new InvalidOperationException("HOARY GORILLA HIJINX");
}
}
public static void Sandbox(Lua thread, Action callback, Action exceptionCallback = null)
{
GetSandbox(thread).Sandbox(callback, exceptionCallback);
}
}
}