diff --git a/src/OneScript.Core/OneScript.Core.csproj b/src/OneScript.Core/OneScript.Core.csproj
index d69d06de6..a66fb39d1 100644
--- a/src/OneScript.Core/OneScript.Core.csproj
+++ b/src/OneScript.Core/OneScript.Core.csproj
@@ -9,6 +9,9 @@
+
+
+
OneScript.CoreLib
diff --git a/src/ScriptEngine.HostedScript/HostedScriptEngine.cs b/src/ScriptEngine.HostedScript/HostedScriptEngine.cs
index 6a26da3d6..979da365a 100644
--- a/src/ScriptEngine.HostedScript/HostedScriptEngine.cs
+++ b/src/ScriptEngine.HostedScript/HostedScriptEngine.cs
@@ -117,6 +117,14 @@ public Process CreateProcess(IHostApplication host, SourceCode src)
return InitProcess(bslProcess, host, module);
}
+ public Process CreateProcess(IHostApplication host, IExecutableModule module)
+ {
+ Initialize();
+ SetGlobalEnvironment(host, module.Source);
+ var bslProcess = _engine.NewProcess();
+ return InitProcess(bslProcess, host, module);
+ }
+
private void DefineConstants(ICompilerFrontend compilerSvc)
{
var definitions = _workingConfig.PreprocessorDefinitions;
diff --git a/src/ScriptEngine/Machine/Core.cs b/src/ScriptEngine/Machine/Core.cs
index 0403ccffe..4ebb6d36b 100644
--- a/src/ScriptEngine/Machine/Core.cs
+++ b/src/ScriptEngine/Machine/Core.cs
@@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/
using System;
+using MessagePack;
namespace ScriptEngine.Machine
{
@@ -141,10 +142,12 @@ public enum OperationCode
ModuleInfo
}
- [Serializable]
+ [MessagePackObjectAttribute]
public struct Command
{
+ [global::MessagePack.Key(0)]
public OperationCode Code;
+ [global::MessagePack.Key(1)]
public int Argument;
public override string ToString()
diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs
index cea8f0c62..e20bdd59f 100644
--- a/src/ScriptEngine/Machine/MachineInstance.cs
+++ b/src/ScriptEngine/Machine/MachineInstance.cs
@@ -660,6 +660,8 @@ private void InitCommands()
#region Simple operations
private void PushVar(int arg)
{
+ if (arg < 0 || _module.VariableRefs == null || arg >= _module.VariableRefs.Count)
+ throw new ScriptException($"Invalid variable-ref index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
var binding = _module.VariableRefs[arg];
var scope = _currentFrame.Scopes[binding.ScopeNumber];
_operationStack.Push(scope.Variables[binding.MemberNumber]);
@@ -668,6 +670,8 @@ private void PushVar(int arg)
private void PushConst(int arg)
{
+ if (arg < 0 || _module.Constants == null || arg >= _module.Constants.Count)
+ throw new ScriptException($"Invalid constant index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
_operationStack.Push(_module.Constants[arg]);
NextInstruction();
}
@@ -698,12 +702,16 @@ private void PushNull(int arg)
private void PushLoc(int arg)
{
+ if (arg < 0 || _currentFrame == null || _currentFrame.Locals == null || arg >= _currentFrame.Locals.Length)
+ throw new ScriptException($"Invalid local index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
_operationStack.Push(_currentFrame.Locals[arg]);
NextInstruction();
}
private void PushRef(int arg)
{
+ if (arg < 0 || _module.VariableRefs == null || arg >= _module.VariableRefs.Count)
+ throw new ScriptException($"Invalid variable-ref index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
var binding = _module.VariableRefs[arg];
var scope = _currentFrame.Scopes[binding.ScopeNumber];
var reference = Variable.CreateContextPropertyReference(scope.Instance, binding.MemberNumber, "$stackvar");
@@ -713,17 +721,19 @@ private void PushRef(int arg)
private void LoadVar(int arg)
{
+ if (arg < 0 || _module.VariableRefs == null || arg >= _module.VariableRefs.Count)
+ throw new ScriptException($"Invalid variable-ref index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
var binding = _module.VariableRefs[arg];
var scope = _currentFrame.Scopes[binding.ScopeNumber];
scope.Variables[binding.MemberNumber].Value = PopRawValue();
NextInstruction();
}
- private void LoadLoc(int arg)
- {
- _currentFrame.Locals[arg].Value = PopRawValue();
- NextInstruction();
- }
+ private void LoadLoc(int arg)
+ {
+ _currentFrame.Locals[arg].Value = PopRawValue();
+ NextInstruction();
+ }
private void AssignRef(int arg)
{
@@ -995,7 +1005,10 @@ private void PushDefaultArg(int arg)
private void ResolveProp(int arg)
{
var objIValue = _operationStack.Pop();
-
+
+ if (arg < 0 || _module.Constants == null || arg >= _module.Constants.Count)
+ throw new ScriptException($"Invalid constant index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
+
var context = objIValue.AsObject();
var propName = _module.Constants[arg].ToString(_process);
var propNum = context.GetPropertyNumber(propName);
@@ -1007,6 +1020,9 @@ private void ResolveProp(int arg)
private void ResolveMethodProc(int arg)
{
+ if (arg < 0 || _module.Constants == null || arg >= _module.Constants.Count)
+ throw new ScriptException($"Invalid constant index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
+
PrepareContextCallArguments(arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues);
context.CallAsProcedure(methodId, argValues, _process);
@@ -1015,6 +1031,9 @@ private void ResolveMethodProc(int arg)
private void ResolveMethodFunc(int arg)
{
+ if (arg < 0 || _module.Constants == null || arg >= _module.Constants.Count)
+ throw new ScriptException($"Invalid constant index {arg} at instruction {_currentFrame?.InstructionPointer} in module {_module?.Source?.Location ?? ""}");
+
PrepareContextCallArguments(arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues);
if (!context.DynamicMethodSignatures && context.GetMethodInfo(methodId).ReturnType == typeof(void))
diff --git a/src/ScriptEngine/Machine/PrecompiledRuntimeModule.cs b/src/ScriptEngine/Machine/PrecompiledRuntimeModule.cs
new file mode 100644
index 000000000..d386df69d
--- /dev/null
+++ b/src/ScriptEngine/Machine/PrecompiledRuntimeModule.cs
@@ -0,0 +1,91 @@
+/*----------------------------------------------------------
+This Source Code Form is subject to the terms of the
+Mozilla Public License, v.2.0. If a copy of the MPL
+was not distributed with this file, You can obtain one
+at http://mozilla.org/MPL/2.0/.
+----------------------------------------------------------*/
+
+using MessagePack;
+using System.IO;
+using System;
+using System.Collections.Generic;
+using OneScript.Contexts;
+using OneScript.Execution;
+using OneScript.Sources;
+
+namespace ScriptEngine.Machine
+{
+
+ [MessagePackObject]
+ public class PrecompiledRuntimeModule : IExecutableModule
+ {
+ [Key(0)]
+ public IList ModuleAttributes { get; set; }
+
+ [Key(1)]
+ public IList Fields { get; set; }
+
+ [Key(2)]
+ public IList Properties { get; set; }
+
+ [Key(3)]
+ public IList Methods { get; set; }
+
+ [Key(4)]
+ public BslScriptMethodInfo ModuleBody { get; set; }
+
+ [Key(5)]
+ public SourceCode Source { get; set; }
+
+ [Key(6)]
+ public IDictionary Interfaces { get; set; }
+ }
+
+ public class ModuleSerializer
+ {
+ public static void SaveModule(IExecutableModule module, string filePath)
+ {
+ SaveModuleDto(module, filePath);
+ }
+
+ public static IExecutableModule LoadModule(string filePath)
+ {
+
+ if (!File.Exists(filePath))
+ throw new FileNotFoundException($"Precompiled module not found: {filePath}");
+
+ byte[] bytes = File.ReadAllBytes(filePath);
+
+ try
+ {
+ var dto = MessagePackSerializer.Deserialize(bytes);
+ if (dto != null)
+ {
+ return RuntimeModuleDtoMapper.FromDto(dto);
+ }
+ }
+ catch (MessagePack.MessagePackSerializationException)
+ {
+ // not DTO format, fall through to legacy deserialization
+ }
+
+ return MessagePackSerializer.Deserialize(bytes);
+ }
+
+ public static void SaveModuleDto(IExecutableModule module, string filePath)
+ {
+ var dto = RuntimeModuleDtoMapper.ToDto(module);
+ byte[] bytes = MessagePackSerializer.Serialize(dto);
+ File.WriteAllBytes(filePath, bytes);
+ }
+
+ public static PrecompiledRuntimeModuleDto LoadModuleDto(string filePath)
+ {
+ if (!File.Exists(filePath))
+ throw new FileNotFoundException($"Precompiled module not found: {filePath}");
+
+ byte[] bytes = File.ReadAllBytes(filePath);
+ return MessagePackSerializer.Deserialize(bytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ScriptEngine/Machine/PrecompiledRuntimeModuleDtos.cs b/src/ScriptEngine/Machine/PrecompiledRuntimeModuleDtos.cs
new file mode 100644
index 000000000..251a890fc
--- /dev/null
+++ b/src/ScriptEngine/Machine/PrecompiledRuntimeModuleDtos.cs
@@ -0,0 +1,800 @@
+/*----------------------------------------------------------
+ DTO classes for PrecompiledRuntimeModule serialization via MessagePack.
+ These are simple POCOs annotated with MessagePack attributes and
+ helper methods to convert runtime types to DTOs.
+----------------------------------------------------------*/
+
+using MessagePack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using OneScript.Contexts;
+using OneScript.Execution;
+using OneScript.Values;
+using OneScript.Sources;
+
+namespace ScriptEngine.Machine
+{
+ [MessagePackObject]
+ public class PrecompiledRuntimeModuleDto
+ {
+ [Key(2)] public string ModuleTypeName { get; set; }
+ // stack-specific
+ [Key(3)] public IList Code { get; set; }
+ [Key(4)] public IList