Oops. Here's the actual new mod_managed files
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14366 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
eb873d8898
commit
e9a46e8942
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_managed
|
||||
* Copyright (C) 2008, Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_managed
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
* Jeff Lenk <jeff@jefflenk.com>
|
||||
*
|
||||
* PluginInterfaces.cs -- Public interfaces for plugins
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace FreeSWITCH {
|
||||
|
||||
public class AppContext {
|
||||
readonly string arguments;
|
||||
readonly Native.ManagedSession session;
|
||||
|
||||
public AppContext(string arguments, Native.ManagedSession session) {
|
||||
this.arguments = arguments;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public string Arguments { get { return arguments; } }
|
||||
public Native.ManagedSession Session { get { return session; } }
|
||||
}
|
||||
|
||||
public class ApiContext {
|
||||
readonly string arguments;
|
||||
readonly Native.Stream stream;
|
||||
readonly Native.Event evt;
|
||||
|
||||
public ApiContext(string arguments, Native.Stream stream, Native.Event evt) {
|
||||
this.arguments = arguments;
|
||||
this.stream = stream;
|
||||
this.evt = evt;
|
||||
}
|
||||
|
||||
public string Arguments { get { return arguments; } }
|
||||
public Native.Stream Stream { get { return stream; } }
|
||||
public Native.Event Event { get { return evt; } }
|
||||
}
|
||||
|
||||
public class ApiBackgroundContext {
|
||||
readonly string arguments;
|
||||
|
||||
public ApiBackgroundContext(string arguments) {
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
public string Arguments { get { return arguments; } }
|
||||
}
|
||||
|
||||
public interface IApiPlugin {
|
||||
void Execute(ApiContext context);
|
||||
void ExecuteBackground(ApiBackgroundContext context);
|
||||
}
|
||||
|
||||
public interface IAppPlugin {
|
||||
void Run(AppContext context);
|
||||
}
|
||||
|
||||
public interface ILoadNotificationPlugin {
|
||||
bool Load();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_managed
|
||||
* Copyright (C) 2008, Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_managed
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
* Jeff Lenk <jeff@jefflenk.com>
|
||||
*
|
||||
* PluginManager.cs -- Plugin execution code
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace FreeSWITCH {
|
||||
|
||||
internal abstract class PluginExecutor : MarshalByRefObject {
|
||||
public override object InitializeLifetimeService() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Names by which this plugin may be executed.</summary>
|
||||
public List<string> Aliases { get { return aliases; } }
|
||||
readonly List<string> aliases = new List<string>();
|
||||
|
||||
/// <summary>The canonical name to identify this plugin (informative).</summary>
|
||||
public string Name { get { return name; } }
|
||||
readonly string name;
|
||||
|
||||
protected PluginExecutor(string name, List<string> aliases) {
|
||||
if (string.IsNullOrEmpty(name)) throw new ArgumentException("No name provided.");
|
||||
if (aliases == null || aliases.Count == 0) throw new ArgumentException("No aliases provided.");
|
||||
this.name = name;
|
||||
this.aliases = aliases.Distinct().ToList();
|
||||
}
|
||||
|
||||
int useCount = 0;
|
||||
protected void IncreaseUse() {
|
||||
System.Threading.Interlocked.Increment(ref useCount);
|
||||
}
|
||||
protected void DecreaseUse() {
|
||||
var count = System.Threading.Interlocked.Decrement(ref useCount);
|
||||
if (count == 0 && onZeroUse != null) {
|
||||
onZeroUse();
|
||||
}
|
||||
}
|
||||
|
||||
Action onZeroUse;
|
||||
public void SetZeroUseNotification(Action onZeroUse) {
|
||||
this.onZeroUse = onZeroUse;
|
||||
if (useCount == 0) onZeroUse();
|
||||
}
|
||||
|
||||
protected static void LogException(string action, string moduleName, Exception ex) {
|
||||
Log.WriteLine(LogLevel.Error, "{0} exception in {1}: {2}", action, moduleName, ex.Message);
|
||||
Log.WriteLine(LogLevel.Debug, "{0} exception: {1}", moduleName, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AppPluginExecutor : PluginExecutor {
|
||||
|
||||
readonly Func<IAppPlugin> createPlugin;
|
||||
|
||||
public AppPluginExecutor(string name, List<string> aliases, Func<IAppPlugin> creator)
|
||||
: base(name, aliases) {
|
||||
if (creator == null) throw new ArgumentNullException("Creator cannot be null.");
|
||||
this.createPlugin = creator;
|
||||
}
|
||||
|
||||
public bool Execute(string args, IntPtr sessionHandle) {
|
||||
IncreaseUse();
|
||||
try {
|
||||
using (var session = new Native.ManagedSession(new Native.SWIGTYPE_p_switch_core_session(sessionHandle, false))) {
|
||||
session.Initialize();
|
||||
session.SetAutoHangup(false);
|
||||
try {
|
||||
var plugin = createPlugin();
|
||||
var context = new AppContext(args, session);;
|
||||
plugin.Run(context);
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
LogException("Run", Name, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
DecreaseUse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ApiPluginExecutor : PluginExecutor {
|
||||
|
||||
readonly Func<IApiPlugin> createPlugin;
|
||||
|
||||
public ApiPluginExecutor(string name, List<string> aliases, Func<IApiPlugin> creator)
|
||||
: base(name, aliases) {
|
||||
if (creator == null) throw new ArgumentNullException("Creator cannot be null.");
|
||||
this.createPlugin = creator;
|
||||
}
|
||||
|
||||
public bool ExecuteApi(string args, IntPtr streamHandle, IntPtr eventHandle) {
|
||||
IncreaseUse();
|
||||
try {
|
||||
using (var stream = new Native.Stream(new Native.switch_stream_handle(streamHandle, false)))
|
||||
using (var evt = eventHandle == IntPtr.Zero ? null : new Native.Event(new Native.switch_event(eventHandle, false), 0)) {
|
||||
try {
|
||||
var context = new ApiContext(args, stream, evt);
|
||||
var plugin = createPlugin();
|
||||
plugin.Execute(context);
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
LogException("Execute", Name, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
DecreaseUse();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ExecuteApiBackground(string args) {
|
||||
// Background doesn't affect use count
|
||||
new System.Threading.Thread(() => {
|
||||
try {
|
||||
var context = new ApiBackgroundContext(args);
|
||||
var plugin = createPlugin();
|
||||
plugin.ExecuteBackground(context);
|
||||
Log.WriteLine(LogLevel.Debug, "ExecuteBackground in {0} completed.", Name);
|
||||
} catch (Exception ex) {
|
||||
LogException("ExecuteBackground", Name, ex);
|
||||
}
|
||||
}).Start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class PluginManager : MarshalByRefObject {
|
||||
public override object InitializeLifetimeService() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ApiPluginExecutor> ApiExecutors { get { return _apiExecutors; } }
|
||||
readonly List<ApiPluginExecutor> _apiExecutors = new List<ApiPluginExecutor>();
|
||||
|
||||
public List<AppPluginExecutor> AppExecutors { get { return _appExecutors; } }
|
||||
|
||||
readonly List<AppPluginExecutor> _appExecutors = new List<AppPluginExecutor>();
|
||||
|
||||
bool isLoaded = false;
|
||||
|
||||
public bool Load(string file) {
|
||||
Console.WriteLine("Loading {0} from domain {1}", file, AppDomain.CurrentDomain.FriendlyName);
|
||||
if (isLoaded) throw new InvalidOperationException("PluginManager has already been loaded.");
|
||||
if (string.IsNullOrEmpty(file)) throw new ArgumentNullException("file cannot be null or empty.");
|
||||
if (AppDomain.CurrentDomain.IsDefaultAppDomain()) throw new InvalidOperationException("PluginManager must load in its own AppDomain.");
|
||||
var res = LoadInternal(file);
|
||||
isLoaded = true;
|
||||
|
||||
res = res && AppExecutors.Count > 0 && ApiExecutors.Count > 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
protected abstract bool LoadInternal(string fileName);
|
||||
|
||||
protected bool RunLoadNotify(Type[] allTypes) {
|
||||
// Run Load on all the load plugins
|
||||
var ty = typeof(ILoadNotificationPlugin);
|
||||
var pluginTypes = allTypes.Where(x => ty.IsAssignableFrom(x) && !x.IsAbstract).ToList();
|
||||
if (pluginTypes.Count == 0) return true;
|
||||
foreach (var pt in pluginTypes) {
|
||||
var load = ((ILoadNotificationPlugin)Activator.CreateInstance(pt, false));
|
||||
if (!load.Load()) {
|
||||
Log.WriteLine(LogLevel.Notice, "Type {0} requested no loading. Assembly will not be loaded.", pt.FullName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void AddApiPlugins(Type[] allTypes) {
|
||||
var iApiTy = typeof(IApiPlugin);
|
||||
foreach (var ty in allTypes.Where(x => iApiTy.IsAssignableFrom(x) && !x.IsAbstract)) {
|
||||
var del = CreateConstructorDelegate<IApiPlugin>(ty);
|
||||
var exec = new ApiPluginExecutor(ty.FullName, new List<string> { ty.FullName, ty.Name }, del);
|
||||
this.ApiExecutors.Add(exec);
|
||||
}
|
||||
}
|
||||
|
||||
protected void AddAppPlugins(Type[] allTypes) {
|
||||
var iAppTy = typeof(IAppPlugin);
|
||||
foreach (var ty in allTypes.Where(x => iAppTy.IsAssignableFrom(x) && !x.IsAbstract)) {
|
||||
var del = CreateConstructorDelegate<IAppPlugin>(ty);
|
||||
var exec = new AppPluginExecutor(ty.FullName, new List<string> { ty.FullName, ty.Name }, del);
|
||||
this.AppExecutors.Add(exec);
|
||||
}
|
||||
}
|
||||
|
||||
#region Unload
|
||||
|
||||
bool isUnloading = false;
|
||||
int unloadCount;
|
||||
System.Threading.ManualResetEvent unloadSignal = new System.Threading.ManualResetEvent(false);
|
||||
void decreaseUnloadCount() {
|
||||
if (System.Threading.Interlocked.Decrement(ref unloadCount) == 0) {
|
||||
unloadSignal.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public void BlockUntilUnloadIsSafe() {
|
||||
if (isUnloading) throw new InvalidOperationException("PluginManager is already unloading.");
|
||||
isUnloading = true;
|
||||
unloadCount = ApiExecutors.Count + AppExecutors.Count;
|
||||
ApiExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount));
|
||||
AppExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount));
|
||||
unloadSignal.WaitOne();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static Func<T> CreateConstructorDelegate<T>(Type ty) {
|
||||
var destTy = typeof(T);
|
||||
if (!destTy.IsAssignableFrom(ty)) throw new ArgumentException(string.Format("Type {0} is not assignable from {1}.", destTy.FullName, ty.FullName));
|
||||
var con = ty.GetConstructor(Type.EmptyTypes);
|
||||
if (con == null) throw new ArgumentException(string.Format("Type {0} doesn't have an accessible parameterless constructor.", ty.FullName));
|
||||
|
||||
var rand = Guid.NewGuid().ToString().Replace("-", "");
|
||||
var dm = new DynamicMethod("CREATE_" + ty.FullName.Replace('.', '_') + rand, ty, null, true);
|
||||
var il = dm.GetILGenerator();
|
||||
il.Emit(OpCodes.Newobj, ty.GetConstructor(Type.EmptyTypes));
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
return (Func<T>)dm.CreateDelegate(typeof(Func<T>));
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsmPluginManager : PluginManager {
|
||||
|
||||
protected override bool LoadInternal(string fileName) {
|
||||
Assembly asm;
|
||||
try {
|
||||
asm = Assembly.LoadFrom(fileName);
|
||||
} catch (Exception ex) {
|
||||
Log.WriteLine(LogLevel.Info, "Couldn't load {0}: {1}", fileName, ex.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure it's a plugin assembly
|
||||
var ourName = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
if (!asm.GetReferencedAssemblies().Any(n => n.Name == ourName)) {
|
||||
Log.WriteLine(LogLevel.Debug, "Assembly {0} doesn't reference FreeSWITCH.Managed, not loading.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// See if it wants to be loaded
|
||||
var allTypes = asm.GetExportedTypes();
|
||||
if (!RunLoadNotify(allTypes)) return false;
|
||||
|
||||
AddApiPlugins(allTypes);
|
||||
AddAppPlugins(allTypes);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_managed
|
||||
* Copyright (C) 2008, Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_managed
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Michael Giagnocavo <mgg@giagnocavo.net>
|
||||
*
|
||||
* ScriptPluginManager.cs -- Dynamic compilation and script execution
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.CodeDom;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
|
||||
namespace FreeSWITCH {
|
||||
|
||||
public enum ScriptContextType {
|
||||
App,
|
||||
Api,
|
||||
ApiBackground,
|
||||
}
|
||||
|
||||
public static class Script {
|
||||
|
||||
[ThreadStatic]
|
||||
internal static ScriptContextType contextType;
|
||||
[ThreadStatic]
|
||||
internal static object context;
|
||||
|
||||
public static ScriptContextType ContextType { get { return contextType; } }
|
||||
|
||||
public static ApiContext GetApiContext() {
|
||||
return getContext<ApiContext>(ScriptContextType.Api);
|
||||
}
|
||||
public static ApiBackgroundContext GetApiBackgroundContext() {
|
||||
return getContext<ApiBackgroundContext>(ScriptContextType.ApiBackground);
|
||||
}
|
||||
public static AppContext GetAppContext() {
|
||||
return getContext<AppContext>(ScriptContextType.App);
|
||||
}
|
||||
|
||||
public static T getContext<T>(ScriptContextType sct) {
|
||||
var ctx = context;
|
||||
if (ctx == null) throw new InvalidOperationException("Current context is null.");
|
||||
if (contextType != sct) throw new InvalidOperationException("Current ScriptContextType is not " + sct.ToString() + ".");
|
||||
return (T)ctx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class ScriptPluginManager : PluginManager {
|
||||
|
||||
protected override bool LoadInternal(string fileName) {
|
||||
Assembly asm;
|
||||
if (Path.GetExtension(fileName).ToLowerInvariant() == ".exe") {
|
||||
asm = Assembly.LoadFrom(fileName);
|
||||
} else {
|
||||
asm = compileAssembly(fileName);
|
||||
}
|
||||
if (asm == null) return false;
|
||||
|
||||
return processAssembly(fileName, asm);
|
||||
}
|
||||
|
||||
Assembly compileAssembly(string fileName) {
|
||||
var comp = new CompilerParameters();
|
||||
var mainRefs = new List<string> {
|
||||
Path.Combine(Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir, "FreeSWITCH.Managed.dll"),
|
||||
"System.dll", "System.Xml.dll", "System.Data.dll"
|
||||
};
|
||||
var extraRefs = new List<string> {
|
||||
"System.Core.dll",
|
||||
"System.Xml.Linq.dll",
|
||||
};
|
||||
comp.ReferencedAssemblies.AddRange(mainRefs.ToArray());
|
||||
comp.ReferencedAssemblies.AddRange(extraRefs.ToArray());
|
||||
CodeDomProvider cdp;
|
||||
var ext = Path.GetExtension(fileName).ToLowerInvariant();
|
||||
switch (ext) {
|
||||
case ".fsx":
|
||||
cdp = CodeDomProvider.CreateProvider("f#");
|
||||
break;
|
||||
case ".csx":
|
||||
cdp = new Microsoft.CSharp.CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v3.5" } });
|
||||
break;
|
||||
case ".vbx":
|
||||
cdp = new Microsoft.VisualBasic.VBCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v3.5" } });
|
||||
break;
|
||||
case ".jsx":
|
||||
// Have to figure out better JS support
|
||||
cdp = CodeDomProvider.CreateProvider("js");
|
||||
extraRefs.ForEach(comp.ReferencedAssemblies.Remove);
|
||||
break;
|
||||
default:
|
||||
if (CodeDomProvider.IsDefinedExtension(ext)) {
|
||||
cdp = CodeDomProvider.CreateProvider(CodeDomProvider.GetLanguageFromExtension(ext));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
comp.GenerateInMemory = true;
|
||||
comp.GenerateExecutable = true;
|
||||
|
||||
Log.WriteLine(LogLevel.Info, "Compiling {0}", fileName);
|
||||
var res = cdp.CompileAssemblyFromFile(comp, fileName);
|
||||
|
||||
var errors = res.Errors.Cast<CompilerError>().Where(x => !x.IsWarning).ToList();
|
||||
if (errors.Count > 0) {
|
||||
Log.WriteLine(LogLevel.Error, "There were {0} errors compiling {1}.", errors.Count, fileName);
|
||||
foreach (var err in errors) {
|
||||
if (string.IsNullOrEmpty(err.FileName)) {
|
||||
Log.WriteLine(LogLevel.Error, "{0}: {1}", err.ErrorNumber, err.ErrorText);
|
||||
} else {
|
||||
Log.WriteLine(LogLevel.Error, "{0}: {1}:{2}:{3} {4}", err.ErrorNumber, err.FileName, err.Line, err.Column, err.ErrorText);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Log.WriteLine(LogLevel.Info, "File {0} compiled successfully.", fileName);
|
||||
return res.CompiledAssembly;
|
||||
}
|
||||
|
||||
bool processAssembly(string fileName, Assembly asm) {
|
||||
var allTypes = asm.GetExportedTypes();
|
||||
if (!RunLoadNotify(allTypes)) return false;
|
||||
|
||||
// Scripts can specify classes too
|
||||
AddApiPlugins(allTypes);
|
||||
AddAppPlugins(allTypes);
|
||||
|
||||
// Add the script executors
|
||||
var entryPoint = getEntryDelegate(asm.EntryPoint);
|
||||
var name = Path.GetFileName(fileName);
|
||||
var aliases = new List<string> { name };
|
||||
this.ApiExecutors.Add(new ApiPluginExecutor(name, aliases, () => new ScriptApiWrapper(entryPoint)));
|
||||
this.AppExecutors.Add(new AppPluginExecutor(name, aliases, () => new ScriptAppWrapper(entryPoint)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class ScriptApiWrapper : IApiPlugin {
|
||||
|
||||
readonly Action entryPoint;
|
||||
public ScriptApiWrapper(Action entryPoint) {
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
|
||||
public void Execute(ApiContext context) {
|
||||
Script.contextType = ScriptContextType.Api;
|
||||
Script.context = context;
|
||||
try {
|
||||
entryPoint();
|
||||
} finally {
|
||||
Script.context = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteBackground(ApiBackgroundContext context) {
|
||||
Script.contextType = ScriptContextType.ApiBackground;
|
||||
Script.context = context;
|
||||
try {
|
||||
entryPoint();
|
||||
} finally {
|
||||
Script.context = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ScriptAppWrapper : IAppPlugin {
|
||||
|
||||
readonly Action entryPoint;
|
||||
public ScriptAppWrapper(Action entryPoint) {
|
||||
this.entryPoint = entryPoint;
|
||||
}
|
||||
|
||||
public void Run(AppContext context) {
|
||||
Script.contextType = ScriptContextType.App;
|
||||
Script.context = context;
|
||||
try {
|
||||
entryPoint();
|
||||
} finally {
|
||||
Script.context = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static Action getEntryDelegate(MethodInfo entryPoint) {
|
||||
if (!entryPoint.IsPublic || !entryPoint.DeclaringType.IsPublic) {
|
||||
Log.WriteLine(LogLevel.Error, "Entry point: {0}.{1} is not public. This may cause errors with Mono.",
|
||||
entryPoint.DeclaringType.FullName, entryPoint.Name);
|
||||
}
|
||||
var dm = new DynamicMethod(entryPoint.DeclaringType.Assembly.GetName().Name + "_entrypoint_" + entryPoint.DeclaringType.FullName + entryPoint.Name, null, null, true);
|
||||
var il = dm.GetILGenerator();
|
||||
var args = entryPoint.GetParameters();
|
||||
if (args.Length > 1) throw new ArgumentException("Cannot handle entry points with more than 1 parameter.");
|
||||
if (args.Length == 1) {
|
||||
if (args[0].ParameterType != typeof(string[])) throw new ArgumentException("Entry point paramter must be a string array.");
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Newarr, typeof(string));
|
||||
}
|
||||
il.EmitCall(OpCodes.Call, entryPoint, null);
|
||||
if (entryPoint.ReturnType != typeof(void)) {
|
||||
il.Emit(OpCodes.Pop);
|
||||
}
|
||||
il.Emit(OpCodes.Ret);
|
||||
return (Action)dm.CreateDelegate(typeof(Action));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue