This project has moved. For the latest updates, please go here.

Evaluating CLR Method as value in script

Dec 24, 2014 at 7:18 PM
Edited Dec 24, 2014 at 7:37 PM
I've been working on my little DOM implementation and am to the point where I am trying to include 3rd party libraries like jQuery. jQuery performs some implementation checks that I can't seem to get around, like:
support.getElementsByClassName = rnative.test(doc.getElementsByClassName);
(where rnative is a regex that is just looking for "[native code]". Is there a way to override the toString() method on ClearScript objects? Perhaps via some sort of Prototype object?
var test = new String(document.getElementsByClassName);
Result: "undefined"
Expected: Something other than undefined and ideally overridden to "function getElementsByClassName() { [native code] }"

Btw, I just found a post about the ScriptEngine.DisableTypeRestriction flag. Awesome! Saved me from asking another annoying question! ClearScript rocks!

Additional: For better worse, every object in my DOM derives from a common, DynamicObject node (to allow support for dynamic properties and my interpretation of prototype objects). I had the thought that perhaps I could use the Dynamic functionality to optionally return a string value or invoke the method based on the context in which it was referenced but that sounds uglier than it already is (and may not work as expected).
Dec 26, 2014 at 1:54 PM
Hey,

is there something specific you need from jQuery? I'm asking because you might evaluate a solution like CheerioJS (https://github.com/cheeriojs/cheerio), that is a core jQuery implementation designed to run in a server environment.
Dec 26, 2014 at 4:09 PM
Well, I was using jQuery as an example since it's a common library. My code will be retrieving and executing scripts from 3rd party servers. I am trying to achieve a high level of compatibility (I have been using PhantomJS and/or Selenium/WebDriver but... this is more fun). I could do fixups and swap out libraries on the fly if I have to but I'd rather not if I can avoid it.
Coordinator
Dec 26, 2014 at 7:19 PM
Edited Dec 27, 2014 at 3:09 AM
Hi krisoye,

Is there a way to override the toString() method on ClearScript objects? Perhaps via some sort of Prototype object? [...] Expected: Something other than undefined and ideally overridden to "function getElementsByClassName() { [native code] }"

Installing a custom toString method for host objects is quite simple. However, producing special output specifically for host methods is a bit tricky because the managed type that represents host methods is internal to ClearScript.

Nevertheless, it can be done with a bit of trickery. Here's a code sample that replaces the built-in toString method for host objects with a function that produces the desired output for host methods:
dynamic setup = engine.Evaluate(@"
    (function (obj, func) {
        obj.constructor.prototype.toString = function() { return func(this); }
    }).valueOf()
");

setup(new object(), new Func<object, string>(obj => {
    var str = obj.ToString();
    if (obj.GetType().FullName == "Microsoft.ClearScript.HostMethod") {
        return "function " + str.Substring(11) + "() { [native code] }";
    }
    return str;
}));
Clearly this is quite hacky and may not work in the future, but hopefully it can provide a quick fix.

Good luck!
Dec 26, 2014 at 8:31 PM
Well, that's pretty slick! V8 seems to run this code unless I attach a debugger. JScript (which seems easier to debug) does not (which complains about constructor.prototype not being defined). If I try to attach Eclipse with this hack in I get an appcash in QTAgent32.exe:
    <ProblemSignatures>
        <EventType>APPCRASH</EventType>
        <Parameter0>QTAgent32.exe</Parameter0>
        <Parameter1>11.0.50727.1</Parameter1>
        <Parameter2>5011df53</Parameter2>
        <Parameter3>StackHash_08ac</Parameter3>
        <Parameter4>0.0.0.0</Parameter4>
        <Parameter5>00000000</Parameter5>
        <Parameter6>c0000005</Parameter6>
        <Parameter7>74e0cb49</Parameter7>
    </ProblemSignatures>
    <DynamicSignatures>
        <Parameter1>6.1.7601.2.1.0.256.48</Parameter1>
        <Parameter2>1033</Parameter2>
        <Parameter22>08ac</Parameter22>
        <Parameter23>08ac7c35000c889c52b861a824ddc684</Parameter23>
        <Parameter24>2ac4</Parameter24>
        <Parameter25>2ac405e344ce12df2b2c2da92f28dde5</Parameter25>
    </DynamicSignatures>
Thanks, though, that does seem to do exactly what I want even if it might not be available in the future.
Dec 26, 2014 at 10:12 PM
Additional: I disabled my own "prototype objects" and was able to step into the debugger. So it must be something in my code that is causing the crash. Thanks again for the workaround!
Coordinator
Dec 27, 2014 at 3:42 AM
Hi again,

Glad to hear that it worked for you, but yes, this solution can only work in V8, as JScript's embedding model is completely different and doesn't involve native proxies that conform to JavaScript's standard prototype scheme.

In fact, there's currently no way to set this up in JScript. You can expose a .NET type that defines toString as a universal extension method, but that doesn't work for host methods because script code is only allowed to invoke them (ClearScript blocks all other operations including member access).

Thanks!