Closed anonhostpi closed 4 months ago
Hi @anonhostpi,
We aren't clear on what this new method would provide over your existing solution. The other ScriptCompiler
methods are already covered via ClearScript's Compile
and CompileDocument
, but V8's CompileFunction
simply creates a function, and ClearScript already gives you a number of ways to do that.
Thanks!
Just asking to level the API with node's, since they frequently use it in their source code.
I think the listed workaround should be fine. I just don't know if it suffers any kind of performance impact.
I know that for one, ClearScript's Compile and CompileDocument don't offer the setting of the with
keyword.
Though, I'm sure that's not a big issue, because node doesn't use it either (and I'm pretty sure the with
keyword is old and deprecated)
Hi @anonhostpi,
Just asking to level the API with node's, since they frequently use it in their source code.
Wrapping V8's CompileFunction
seems unnecessary. Script compilation can be useful, since a compiled script can be re-executed in another context without recompilation, and there's no other way to create such an object. Script functions, on the other hand, are always context-bound in V8, and there are already multiple ways to create them.
I think the listed workaround should be fine. I just don't know if it suffers any kind of performance impact.
It might be slightly worse than a dedicated path to V8's CompileFunction
, but since the function is being created presumably for frequent re-execution, that one-time impact should be negligible. The other thing the workaround is missing is support for compilation cache data, but you could get that by using a compiled script to create the function.
I know that for one, ClearScript's Compile and CompileDocument don't offer the setting of the
with
keyword.
Support for "context extensions" (equivalent to with
) applies only to function compilation, and you could add that to the workaround. Consider the following CompileFunction
extension method:
public static class V8ScriptEngineExtensions {
public static ScriptObject CompileFunction(this V8ScriptEngine engine, IEnumerable<string> argNames, string body, params object[] extensions) {
var create = (ScriptObject)engine.Evaluate(@$"
(function (...extensions) {{
with (Object.assign({{}}, ...extensions))
return function ({string.Join(", ", argNames)}) {{ {body} }};
}})
");
return (ScriptObject)create.InvokeAsFunction(extensions);
}
}
With that in place, you could do something like this:
dynamic greet = engine.CompileFunction(
[ "audience" ],
"Console.WriteLine(format, audience);",
new { Console = typeof(Console).ToHostType(engine), format = "Hello, {0}!" }
);
greet("world"); // prints "Hello, world!"
greet("friends"); // prints "Hello, friends!"
That's just one C# example, but hopefully it gives you an idea.
Good luck!
I was thinking along the same lines.
It does seem that Node does use CompileFunction specifically for context-specific compilation. They do also cache any of their compilations (context-specific or not).
It appears they use it as an optimized substitute for require
It appears they use it as an optimized substitute for
require
Oh, interesting! CommonJS modules depend on things like require
and exports
. The usual way to inject those is to wrap the module code within a function that takes them as arguments. That's what ClearScript does, as it works for both V8 and JScript, but CompileFunction
could be a better solution for V8.
The above sample should work as long as V8 supports the deprecated with
statement, whereas V8's CompileFunction
would presumably continue working after that. Actually, we can modify the above so that it doesn't depend on with
:
public static class V8ScriptEngineExtensions {
public static ScriptObject CompileFunction(this V8ScriptEngine engine, IEnumerable<string> argNames, string body, params object[] extensions) {
var createContext = (ScriptObject)engine.Evaluate(@"
(function (...extensions) {
return Object.assign({}, ...extensions);
})
");
var context = (IDictionary<string, object>)createContext.InvokeAsFunction(extensions);
var createFunction = (ScriptObject)engine.Evaluate(@$"
(function (context) {{
const {{ {string.Join(", ", context.Keys)} }} = context;
return function ({string.Join(", ", argNames)}) {{ {body} }};
}})
");
return (ScriptObject)createFunction.InvokeAsFunction(context);
}
}
Anyway, please feel free to reopen this issue if you have additional thoughts or questions about this topic. Thank you!
I've been working on porting Node.JS APIs over to PowerShell via ClearScript:
I've noticed that Node.JS uses V8's CompileFunction a lot for the underlying code behind
internalBinding()
. I also suspect that it is used for thevm.compileFunction()
.For now, I think I can just get away with something similar to:
Could you guys please expose the V8 compiler or the rest of its functions?