Is there a way to enable "import <module>.js;" statements in an embedded V8 Runtime using Microsoft ClearScript?
I couldn't find any examples and makes me think I'd have to parse the script file myself first to enable this.
Yes there is. See answer to this question as the basic approach. That question was about enabling CommonJS Require. For ES6 modules approach you will want something like this:
using System;
using Microsoft.ClearScript;
using Microsoft.ClearScript.JavaScript;
using Microsoft.ClearScript.V8;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
V8ScriptEngine engine = new V8ScriptEngine();
engine.DocumentSettings.SearchPath = #"c:\temp\js\";
engine.DocumentSettings.AccessFlags = DocumentAccessFlags.EnableFileLoading;
engine.AddHostType(typeof(Console));
Console.WriteLine("Hello from C#");
engine.Execute(new DocumentInfo() { Category = ModuleCategory.Standard }, #"
Console.WriteLine('Hello from Javascript');
import {print} from 'a.js';
import {add} from 'b.js';
print(add(30, 12));
Console.WriteLine('Javascript signing off...');
");
Console.ReadKey();
}
}
}
Where the JS looks like this:
// File c:\temp\js\a.js
export function print(txt) {
Console.WriteLine('The answer to Life etc is ... ' + txt);
}
// File c:\temp\js\b.js
export function add(var1, var2) {
return var1 + var2;
}
Result is:
This example is reading files from the local file system as indicated by the AccessFlags = EnableFileLoading setting, with the SearchPath setting giving the path, or paths if given a comma-separated list of paths.
It is the 'ModuleCategory.Standard' option that enables ES6-stylee module loading.
You could also use a roll-your-own require() function similar to node.js. I wrote a blog article here and there's a working CodePen to illustrate the approach which was inspired by an article by Michele Nasti which was itself based on the ideas in chapter 10 of the book Eloquent JavaScript by Marijn Haverbeke.
Here is my coded example:
const myCode1 = `
let utils = function (){
this.say = function(x){
log('utils: says = ' + x)
};
return this;
}
exports.utils = utils;
`;
const myCode2 = `
let customer = function (){
this.say = function(x){
log('Customer: says = ' + x)
};
return this;
}
exports.customer = customer;
`;
/*
*/
// I am loading simple source code strings into an array - in the real solution
// you would use some kind of source code fetch / load to string process.
let sourceCode = {c1: myCode1, c2: myCode2};
myRequire.cache = Object.create(null);
function myRequire(name) {
log(`myRequire: You require file ${name}`)
if (!(name in myRequire.cache)) {
log(`myRequire: ${name} is not in cache; reading from disk`)
let code = sourceCode[name]; // load the code - this is bespoke to your env
let module = {exports: {}};
myRequire.cache[name] = module;
let wrapper = Function("require, exports, module", code);
wrapper(myRequire, module.exports, module);
}
log(`myRequire: ${name} is in cache. Returning it...`)
return myRequire.cache[name].exports;
}
// myRequire() produces an object from the 'exports' object in the loaded code.
//let myExports = new myRequire('c1');
// we can then refer to these as
// let util = myExports.utils();
// or just use
// let util = new myRequire('c1').utils();
// So...Require c1 will create an object with exports.
let util = new myRequire('c1').utils();
util.say('I am alive!')
log("");
// Require c2 will create an object with exports.
let cust = new myRequire('c2').customer();
cust.say('I am alive too!')
function log(msg){
$('#log').html($('#log').html() + "<br />" + msg);
}
The output is
myRequire: You require file c1
myRequire: c1 is not in cache; reading from disk
myRequire: c1 is in cache. Returning it...
utils: says = I am alive!
myRequire: You require file c2
myRequire: c2 is not in cache; reading from disk
myRequire: c2 is in cache. Returning it...
Customer: says = I am alive too!
Related
I work in a research group and I've been tasked with adding in scripting functionality to a data acquisition program. Ideally, I want to have the ability to write scripts while the data acquisition software is running (and save those scripts as files on the go). A command line might also be nice.
I'm not very experienced at all with C#, but I do have quite a bit of coding experience in other language (Objective-C, Python). I saw this link https://blogs.msdn.microsoft.com/cdndevs/2015/12/01/adding-c-scripting-to-your-development-arsenal-part-1/ which details the "Roselyn Scripting Package" but I'm not sure if that's my best option.
Can anybody suggest the easiest way of getting full scripting functionality? (I'm trying to avoid losing months of my life here =p). Links to start at/advice is much appreciated.
Thanks!
You posted an interesting link for me because I just prototyped something like that some weeks ago, but have not implemented yet. My goal is to create a C# "immediate" console on a webpage.
Have some minor issues regarding loading some assemblies programatically and have to reference them explicitly.
Here is the code behind, please later post your solution, I would be interested to know.
This alows to write c# code at runtime and also get a String return.
protected void getImmediateResult_Click(object sender, EventArgs e)
{
//building the code
string source = #"using System;
class MyType{
public static String Evaluate(){
<!expression!>
}}";
string expression = this.txtimmediate.Text;
string finalSource = source.Replace("<!expression!>", expression);
textcodeCheck.Text = finalSource;
var compileUnit = new CodeSnippetCompileUnit(finalSource);
//preparing compilation
CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
// Create the optional compiler parameters
//this correctly references the application but no System.Web etc
string[] refArray = new string[2];
UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
refArray[0] = uri.Path;
//this works
refArray[1] = "System.Web" + ".dll";
////NOT WORKING for non microsoft assemblies
//var allRefs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
//string[] refArray = new string[allRefs.Length + 1];
//int i = 1;
//foreach (AssemblyName refer in allRefs)
//{
// refArray[i] = refer.Name + ".dll";
// i++;
//}
var compilerParameters = new CompilerParameters(refArray);
CompilerResults compilerResults = provider.CompileAssemblyFromDom(compilerParameters, compileUnit);
if (compilerResults.Errors.Count > 0)
{
//1st error
this.txtResult.Text = compilerResults.Errors[0].ErrorText;
return;
}
//running it
Type type = compilerResults.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");
String result = (String)method.Invoke(null, null);
this.txtResult.Text = result;
}
If you're willing to use IronPython, you can execute scripts directly in C#:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
private static void doPython()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(#"test.py");
}
Get IronPython here.
Since CompileAssemblyFromSource add custom functions in a smart way was ignored im going to ask this question differently so people will bother to read it.
cutting at the chase,i am making a language by "translating" the new syntax into c# and compiling it in memory in this fashion.
using (Microsoft.CSharp.CSharpCodeProvider CodeProv =
new Microsoft.CSharp.CSharpCodeProvider())
{
CompilerResults results = CodeProv.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true
},
code);
var type = results.CompiledAssembly.GetType("MainClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine(output);
}
basically i am executing a "main" function written inside the code variable.
and i am using some functions in the code variable i would like to include without adding it as a string at the bottom like this:
code += #"public void Write(string path, object thevar)
{
if (thevar.GetType() == typeof(string))
{
System.IO.File.WriteAllText(path,(string)thevar);
}
if (thevar.GetType() == typeof(string[]))
{
System.IO.File.WriteAllLines(path,(string[])thevar);
}
}";
Can i somehow add a class from my Actual main project in VS and let the compiled in memory code access it? without adding it as a string.
You can embed your source code file(s) as resources. With this technique you can edit the file in Visual Studio and access the contents of the files as if it was a string during run-time.
This link shows how to do it:
https://stackoverflow.com/a/433182/540832
On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.
Sounds a little bit scary isn't it?
Some background information, I want to load a tar archive which contains some lua modules into my C# application using LuaInterface. The easiest way would be to extract these files to a temp folder, modify the lua module search path and read them with require as usual. But I do not want to put these scripts somewhere on the file system.
So I thought it should be possible to load the tar-archive with the #ziplib I know there are a lot of lua implementations for tar and stuff like that. But the #zlib is already part of the project.
After successfully loading the file as strings(streams) out of the archive I should be able to pass them into lua.DoString(...) in C# via LuaInterface.
But simply loading modules by a dostring or dofile does not work if modules have a line like this: "module(..., package.seeall)" There is a error reportet like passing argument 1 a nil, but string expected.
The other problem is a module may depend on other modules which are also located in the tar archive.
One possible solution should be to define a custom loader as described here.
My idea is to implement such a loader in C# with the #ziplib and map this loader into the lua stack of my C# application.
Does anyone of you had a similar task to this?
Are there any ready to use solutions which already address problems like this?
The tar file is not must have but a nice to have package format.
Is this idea feasible or totally unfeasible?
I've written some example class to extract the lua files from the archive. This method works as loader and return a lua function.
namespace LuaInterfaceTest
{
class LuaTarModuleLoader
{
private LuaTarModuleLoader() { }
~LuaTarModuleLoader()
{
in_stream_.Close();
}
public LuaTarModuleLoader(Stream in_stream,Lua lua )
{
in_stream_ = in_stream;
lua_ = lua;
}
public LuaFunction load(string modulename, out string error_message)
{
string lua_chunk = "test=hello";
string filename = modulename + ".lua";
error_message = "Unable to locate the file";
in_stream_.Position = 0; // rewind
Stream gzipStream = new BZip2InputStream(in_stream_);
TarInputStream tar = new TarInputStream(gzipStream);
TarEntry tarEntry;
LuaFunction func = null;
while ((tarEntry = tar.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
if (filename == tarEntry.Name)
{
MemoryStream out_stream = new MemoryStream();
tar.CopyEntryContents(out_stream);
out_stream.Position = 0; // rewind
StreamReader stream_reader = new StreamReader(out_stream);
lua_chunk = stream_reader.ReadToEnd();
func = lua_.LoadString(lua_chunk, filename);
string dum = func.ToString();
error_message = "No Error!";
break;
}
}
return func;
}
private Stream in_stream_;
private Lua lua_;
}
}
I try to register the load method like this in the LuaInterface
Lua lua = new Lua();
GC.Collect();
Stream inStream = File.OpenRead("c:\\tmp\\lua_scripts.tar.bz2");
LuaTarModuleLoader tar_loader = new LuaTarModuleLoader(inStream, lua);
lua.DoString("require 'CLRPackage'");
lua.DoString("import \"ICSharpCode.SharpZipLib.dll\"");
lua.DoString("import \"System\"");
lua["container_module_loader"] = tar_loader;
lua.DoString("table.insert(package.loaders, 2, container_module_loader.load)");
lua.DoString("require 'def_sensor'");
If I try it this way I'll get an exception while the call to require :
"instance method 'load' requires a non null target object"
I tried to call the load method directly, here I have to use the ":" notation.
lua.DoString("container_module_loader:load('def_sensor')");
If I call the method like that I hit a breakpoint in the debugger which is place on top of the method so everything works as expected.
But If I try to register the method with ":" notation I get an exception while registering the method:
lua.DoString("table.insert(package.loaders, 2, container_module_loader:load)");
"[string "chunk"]:1: function arguments expected near ')'"
In LÖVE they have that working. All Lua files are inside one zip file, and they work, even if ... is used. The library they use is PhysicsFS.
Have a look at the source. Probably /modules/filesystem will get you started.
I finally got the trick ;-)
One Problem I currently not really understand is that my loader should not return any string.
Here is my solution:
The loader Class itself:
namespace LuaInterfaceTest
{
class LuaTarModuleLoader
{
private LuaTarModuleLoader() { }
~LuaTarModuleLoader()
{
in_stream_.Close();
}
public LuaTarModuleLoader(Stream in_stream,Lua lua )
{
in_stream_ = in_stream;
lua_ = lua;
}
public LuaFunction load(string modulename)
{
string lua_chunk = "";
string filename = modulename + ".lua";
in_stream_.Position = 0; // rewind
Stream gzipStream = new BZip2InputStream(in_stream_);
TarInputStream tar = new TarInputStream(gzipStream);
TarEntry tarEntry;
LuaFunction func = null;
while ((tarEntry = tar.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
if (filename == tarEntry.Name)
{
MemoryStream out_stream = new MemoryStream();
tar.CopyEntryContents(out_stream);
out_stream.Position = 0; // rewind
StreamReader stream_reader = new StreamReader(out_stream);
lua_chunk = stream_reader.ReadToEnd();
func = lua_.LoadString(lua_chunk, modulename);
string dum = func.ToString();
break;
}
}
return func;
}
private Stream in_stream_;
private Lua lua_;
}
}
And how to use the loader, I am not sure if all the package stuff is really needed. But I had to wrap up the call with ":" notation and hide it behind my "load_wrapper" function.
string load_wrapper = "local function load_wrapper(modname)\n return container_module_loader:load(modname)\n end";
Lua lua = new Lua();
GC.Collect();
Stream inStream = File.OpenRead("c:\\tmp\\lua_scripts.tar.bz2");
LuaTarModuleLoader tar_loader = new LuaTarModuleLoader(inStream, lua);
lua.DoString("require 'CLRPackage'");
lua.DoString("import \"System\"");
lua["container_module_loader"] = tar_loader;
lua.DoString(load_wrapper);
string loader_package = "module('my_loader', package.seeall) \n";
loader_package += load_wrapper + "\n";
loader_package += "table.insert(package.loaders, 2, load_wrapper)";
lua.DoString(loader_package);
lua.DoFile("./load_modules.lua");
I hope this may also helps some other
I'm attempting to re-use some ruby classes I wrote a while back within an ASP.NET MVC 2 project. The issue I'm having is if a class is within a Module I can't seem to instantiate it. If I move the class outside of the module it works fine. Here is a scaled down version of the class I want to instantiate:
module Generator
class CmdLine
attr_accessor :options
def initialize(output)
end
def run(args=[])
end
end
end
If comment out the module portion I can create the object. Am I doing something wrong? Here is the C# code:
var engine = Ruby.CreateEngine();
var searchPaths = engine.GetSearchPaths().ToList();
searchPaths.Add(#"c:\code\generator\lib");
searchPaths.Add(#"C:\Ruby-ri-192\lib\ruby\1.9.1");
engine.SetSearchPaths(searchPaths);
engine.ExecuteFile(#"c:\code\generator\lib\generator\generator_cmd_line.rb");
var rubyCmdLineObj = engine.Runtime.Globals.GetVariableNames();
// These lines works when I comment out the module
// var genCmdLineObj = engine.Runtime.Globals.GetVariable("CmdLine");
// var cmdLineObj = engine.Operations.CreateInstance(genCmdLineObj);
// var results = engine.Operations.InvokeMember(cmdLineObj, "run");
// return Content(results);
var sb = new StringBuilder();
foreach (var name in rubyCmdLineObj)
{
sb.AppendFormat("{0} ", name);
}
return Content(sb.ToString());
I have a work around - creating a separate class that I can call from within C# but if I don't have to do that I'd rather not do it. Any guidance would be greatly appreciated.
I know this is kind of a hack/workaround, but I managed to do it this way:
Add the next code to the end of your ruby file:
def hack(s)
eval(s)
end
Now your C# code would look like that:
var engine = Ruby.CreateEngine();
var scope = engine.ExecuteFile(#"c:\code\generator\lib\generator\generator_cmd_line.rb");
var genCmdLineObj = engine.Execute(String.Format("hack('{0}::{1}')", "Generator", "CmdLine"), scope);
var cmdLineObj = engine.Operations.CreateInstance(genCmdLineObj);
var results = engine.Operations.InvokeMember(cmdLineObj, "run");
return Content(results);
Kind of a hack, but hey, it works! :)
I would create a new IronRuby project, take your original Ruby code and port/compile it into a .NET library.
No more need to call out at all. You have something that can be called natively from C#.
The solution #Shay Friedman proposed is unnecessary.
gen.rb
module Generator
class CmdLine
attr_accessor :options
def initialize(output)
#output = output
end
def run(args=[])
puts "Hello from cmdLine with #{#output} #{args}"
end
end
end
csharp
void Main()
{
var engine = Ruby.CreateEngine();
var buffer = new MemoryStream();
engine.Runtime.IO.SetOutput(buffer, new StreamWriter(buffer));
engine.ExecuteFile(#"c:\temp\gen.rb");
ObjectHandle handle = engine.ExecuteAndWrap("Generator::CmdLine.new('the output')");
var scope = engine.CreateScope();
scope.SetVariable("myvar", handle);
engine.Execute("myvar.run", scope);
engine.Operations.InvokeMember(handle.Unwrap(), "run", "InvokeMember");
buffer.Position = 0;
Console.WriteLine(new StreamReader(buffer).ReadToEnd());
}
output
Hello from cmdLine with the output []
Hello from cmdLine with the output InvokeMember