Comments (10)
If you just want to compile some javascript once, and then run it multiple times, you can use the ScriptEngine.Compile()
method. If you want something that persists across application restarts, then indeed you need some way to save a DLL. Unfortunately, there is no built-in support for this. There is some hacky code here though: https://github.com/paulbartrum/jurassic/blob/master/Benchmarker/Program.cs (check out SaveDLL method, which is in the commented code at the bottom of the file).
from jurassic.
I am looking for something that persists across application restarts, so compiling to a dll would be preferred.
Using the code you mentioned above I have managed to save a DLL. Am I right in saying EnableDebugging = true
causes IL to be generated as soon as the code is loaded, rather than when used for the first time? Also, what does EnableILAnalysis = true
do?
I do have a problem using the generated DLL. What seems to be generated is a number of JavaScriptClass classes, which I assume correspond to each of the definitions in the source JavaScript. What would probably be most helpful is to return a ScriptEngine preloaded with the compiled JavaScript. Would this be possible?
from jurassic.
EnableDebugging = true
switches to an alternate way of generating the dynamic in-memory assembly. Technically it uses AppDomain.DefineDynamicAssembly
rather than the default, which is to use the somewhat faster DynamicMethod
type. DynamicMethod
doesn't support support DLL serialization, hence the need for EnableDebugging
to be true.
EnableILAnalysis = true
turns on logging of the generated MSIL/CIL. It is not needed in your scenario.
You can get a populated ScriptEngine by creating a new ScriptEngine and then running the generated class method that corresponds to the global code in your script (JavaScriptClass0.global_
?) and passing in your script engine. I'm not sure how else this could possibly work?
from jurassic.
Now I think about how else it could work, neither am I.
I looked around in JavaScriptClass0
and found the global_
function. It has the following signature:
public static object global_(ScriptEngine scriptEngine, Scope scope, object obj)
Obviously scriptEngine
is the engine I want to populate. However, what do scope
and obj
refer to?
What I have at the moment gives an exception. I am trying to run a simple test where the following:
var test = function() { return "test"; };
is compiled using the ScriptCompiler.Save()
in the fork here and the resulting dll is loaded using ScriptCompiler.Load()
. I have had a guess at the arguments, so they may well(probably) be incorrect.
Doing this gives me the exception shown below
Unhandled Exception: System.InvalidOperationException: Internal error: no generated method cache available.
Any ideas? From what I can see from peaking in the code is that the Jurassic.Compiler.GeneratedMethod.Save()
method is not called and therefore Jurassic.Compiler.GeneratedMethod.generatedMethodCache
dictionary is not initialized.
In case its of any use, the decompiled IL looks like this:
using Jurassic;
using Jurassic.Compiler;
using Jurassic.Library;
using System;
public class JavaScriptClass0
{
public JavaScriptClass0()
{
}
public static object global_(ScriptEngine scriptEngine, Scope scope, object obj)
{
object obj1 = null;
int num = 0;
((ObjectScope)scope).ScopeObject.DefineProperty("test", new PropertyDescriptor(Undefined.Value, PropertyAttributes.Writable | PropertyAttributes.Enumerable), false);
UserDefinedFunction userDefinedFunction = new UserDefinedFunction(scriptEngine.Function.InstancePrototype, "", new string[0], scope, " return \"test\"; ", GeneratedMethod.Load(0L), false);
userDefinedFunction.SetPropertyValue("displayName", "test", false);
object obj2 = userDefinedFunction;
ObjectInstance scopeObject = ((ObjectScope)scope).ScopeObject;
if (obj1 == scopeObject.InlineCacheKey)
{
scopeObject.InlinePropertyValues[num] = obj2;
}
else
{
scopeObject.InlineSetPropertyValue("test", obj2, false, out num, out obj1);
}
return Undefined.Value;
}
}
from jurassic.
Ugh, I told you this was hacky :-/
The scope
parameter is used to manage variable scopes and lifetimes -- try calling this method to get a suitable one: Jurassic.Compiler.ObjectScope.CreateRuntimeScope(null, engine.Global, false, false);
.
The obj
parameter I think represents the value of this
... just pass in engine.Global
, that should work.
from jurassic.
I've tried this and the same exception comes up. It seems to me that not all the information in the original ScriptEngine
is contained within the generated dll. It seems that the dictionary of function references is not captured in the IL. Would this be right?
from jurassic.
It's likely the code is assuming that you can't run any compiled JS code without first compiling it. As a workaround, you could try compiling something prior to running the DLL code e.g. engine.Execute("")
.
from jurassic.
I am still getting the same exception. This maybe harder than we first thought.
from jurassic.
I had a play around and got a little further. It appears that the method cache Jurassic.Compiler.GeneratedMethod.generatedMethodCache
is not repopulated by any method outputted in the IL. I am using the code below to create the DLL with the ScriptCompiler.Save()
method here. Please ignore the comments, I was playing with Reflection.Emit.
private static void JurassicCompileTest()
{
var sw = new Stopwatch();
// Create an engine.
var compiler = new ScriptCompiler();
// Create a Javscript object using Javscript object initializer.
sw.Restart();
compiler.IncludeInput("var test = { 'foo' : 'bar', 'bar':'foo' };");
compiler.IncludeInput("var helloWorldAndTest = function() { return JSON.stringify(test); };");
Console.WriteLine(sw.ElapsedMilliseconds / 1000.0);
compiler.Save("Test.dll");
}
I've had some success when I try to repopulate manually using the code below which uses the ScriptCompiler.Load()
method.
private static void CompiledJurassicTest()
{
var sw = new Stopwatch();
// Create an engine.
var compiler = new ScriptCompiler();
var engine = compiler.Load(
new Func<ScriptEngine, Scope, object, object>[] { JavaScriptClass0.global_, JavaScriptClass1.global_ },
new[] { new FunctionDelegate((a, b, c, d, e) => JavaScriptClass2.anonymous(a, b, c, d, e)) });
engine.SetGlobalFunction("log", new Action<string>(Console.WriteLine));
Console.WriteLine(engine.CallGlobalFunction("helloWorldAndTest"));
}
Does this seem a reasonable thing to do? I am thinking of
- In the Load method using reflection to find all
.anonymous
methods and add them to theGeneratedMethod.generatedMethodCache
and then find the '.global_' methods in each type and run them on the ScriptEngine.
from jurassic.
Hi, I have managed to get something working by recreating the method cache. Compiling a DLL using .Save()
create an assembly with a RestoreScriptEngine()
method that can be called to get the saved engine back. Disappointingly, this call appears to take slightly longer than just executing the original script. Running the global_()
methods seem to take the majority of the time, I do not know whether it would be possible for that step to be shortened.
from jurassic.
Related Issues (20)
- 3.2.1 backward compatibility issues HOT 4
- PropertyAccessorValue not visible HOT 1
- How to catch CLR Exception? HOT 7
- System.ArgumentNullException: Value cannot be null. (Parameter 'value') after update from 3.1.0 to 3.2.2 HOT 3
- JSONObject.Parse() fails when number is in scientific notation HOT 4
- ScriptEngine.EnableDebugging HOT 3
- Template of literals is slow
- JSON.stringify([].length) errors HOT 2
- JSONObject.Parse consumes more and more memory. Possible memory leak? HOT 11
- Scoping and CompiledScript.Execute vs ScriptEngine.Evaluate HOT 5
- Catching script errors at compile time HOT 2
- Currently JSONObject.Stringify returns object instead of string after upgrading from 3.1.0 to 3.2.6 HOT 3
- ScriptEngine.GetGlobalValue HOT 1
- Bug in Evaluate: Loop and variable handling HOT 2
- PM - Customer side scripting for Channels HOT 1
- Date.UTC() doesn't interpret two-digit years correctly HOT 1
- Possibility to create 'sessions' HOT 4
- Linux compability ? HOT 4
- Is it possible to go from JS to DLL and then to C# source? HOT 2
- Automatic conversion from C# to Jurassic JavaScript HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from jurassic.