public void OnLoad(ConfigNode node) { try { var scriptBuilder = new StringBuilder(); foreach (ConfigNode contextNode in node.GetNodes("context")) { foreach (ConfigNode varNode in contextNode.GetNodes("variables")) { foreach (ConfigNode.Value value in varNode.values) { string varValue = PersistenceUtilities.DecodeLine(value.value); scriptBuilder.AppendLine(string.Format("set {0} to {1}.", value.name, varValue)); } } } if (shared.ScriptHandler == null || scriptBuilder.Length <= 0) return; var programBuilder = new ProgramBuilder(); // TODO: figure out how to store the filename and reload it for arg 1 below: // (Possibly all of OnLoad needs work because it never seemed to bring // back the context fully right anyway, which is why this hasn't been // addressed yet). try { SafeHouse.Logger.Log("Parsing Context:\n\n" + scriptBuilder); // TODO - make this set up compiler options and pass them in properly, so we can detect built-ins properly. // (for the compiler to detect the difference between a user function call and a built-in, it needs to be // passed the FunctionManager object from Shared.) // this isn't fixed mainly because this OnLoad() code is a major bug fire already anyway and needs to be // fixed, but that's way out of scope for the moment: programBuilder.AddRange(shared.ScriptHandler.Compile("reloaded file", 1, scriptBuilder.ToString())); List<Opcode> program = programBuilder.BuildProgram(); RunProgram(program, true); } catch (NullReferenceException ex) { SafeHouse.Logger.Log("program builder failed on load. " + ex.Message); } } catch (Exception e) { if (shared.Logger != null) shared.Logger.Log(e); } }
public override void Execute(SharedObjects shared) { // run() is strange. It needs two levels of args - the args to itself, and the args it is meant to // pass on to the program it's invoking. First, these are the args to run itself: object volumeId = PopValueAssert(shared, true); string fileName = PopValueAssert(shared, true).ToString(); AssertArgBottomAndConsume(shared); // Now the args it is going to be passing on to the program: var progArgs = new List<Object>(); int argc = CountRemainingArgs(shared); for (int i = 0; i < argc; ++i) progArgs.Add(PopValueAssert(shared, true)); AssertArgBottomAndConsume(shared); if (shared.VolumeMgr == null) return; if (shared.VolumeMgr.CurrentVolume == null) throw new Exception("Volume not found"); ProgramFile file = shared.VolumeMgr.CurrentVolume.GetByName(fileName, true); if (file == null) throw new Exception(string.Format("File '{0}' not found", fileName)); if (shared.ScriptHandler == null) return; if (volumeId != null) { Volume targetVolume = shared.VolumeMgr.GetVolume(volumeId); if (targetVolume != null) { if (shared.ProcessorMgr != null) { string filePath = string.Format("{0}/{1}", shared.VolumeMgr.GetVolumeRawIdentifier(targetVolume), fileName); var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; List<CodePart> parts = shared.ScriptHandler.Compile(filePath, 1, file.StringContent, "program", options); var builder = new ProgramBuilder(); builder.AddRange(parts); List<Opcode> program = builder.BuildProgram(); shared.ProcessorMgr.RunProgramOn(program, targetVolume); } } else { throw new KOSFileException("Volume not found"); } } else { // clear the "program" compilation context shared.ScriptHandler.ClearContext("program"); string filePath = shared.VolumeMgr.GetVolumeRawIdentifier(shared.VolumeMgr.CurrentVolume) + "/" + fileName; var options = new CompilerOptions { LoadProgramsInSameAddressSpace = true, FuncManager = shared.FunctionManager }; var programContext = ((CPU)shared.Cpu).SwitchToProgramContext(); List<CodePart> codeParts; if (file.Category == FileCategory.KSM) { string prefix = programContext.Program.Count.ToString(); codeParts = shared.VolumeMgr.CurrentVolume.LoadObjectFile(filePath, prefix, file.BinaryContent); } else { try { codeParts = shared.ScriptHandler.Compile(filePath, 1, file.StringContent, "program", options); } catch (Exception) { // If it died due to a compile error, then we won't really be able to switch to program context // as was implied by calling Cpu.SwitchToProgramContext() up above. The CPU needs to be // told that it's still in interpreter context, or else it fails to advance the interpreter's // instruction pointer and it will just try the "call run()" instruction again: shared.Cpu.BreakExecution(false); throw; } } programContext.AddParts(codeParts); } // Because run() returns FIRST, and THEN the CPU jumps to the new program's first instruction that it set up, // it needs to put the return stack in a weird order. Its return value needs to be buried UNDER the args to the // program it's calling: UsesAutoReturn = false; shared.Cpu.PushStack(0); // dummy return that all functions have. // Put the args for the program being called back on in the same order they were in before (so read the list backward): shared.Cpu.PushStack(new KOSArgMarkerType()); for (int i = argc - 1; i >= 0; --i) shared.Cpu.PushStack(progArgs[i]); }