DreamSpaceJS and DreamSpace Server

Feb 28, 2013 at 3:08 PM
Edited Feb 28, 2013 at 3:36 PM
Hi, you asked for some feed back, so I'm creating this thread to do so. :)

Background: I've been toying with the idea of creating an entirely browser run development IDE using JavaScript for the purposes of gaming AND web design (any web app really - and yes, I'm aware of other HTML game and web app building solutions). I have experience in doing something similar years ago, and had registered domain names, but never had time to go forward (and frankly, the technology support just wasn't where I wanted it). I saw the entry of NodeJS and was excited at first, but until the NodeIIS wrapper came out, I was blocked by my clients (I run a company developing healthcare solutions for hospitals) frowning on anything NOT running in IIS (ASP+MVC, jQuery, and related - many hospitals has MSDN subscriptions, and this their I.T. teams run on such related products). Now, with TypeScript, and a JavaScript based compiler + language service, and a way to run a JavaScript server of my own within .NET, I feel the time is ripe to give it a try. :)

DreamSpaceJS is a web app (HTML5 games, websites, etc.) building framework I'm writing in JS. The end goal is to create a browser editor that allows editing the client AND server side JavaScript at the SAME time, hence the need for a JavaScript server - but not just any server! The server needs to be a high-speed multi-user end point to support many thousands of users, and I wanted to make sure I had (somewhat) full control over how it tightly integrated with the V8 engine (and, a bonus as it would be, the support of other script languages ;) ).
Feb 28, 2013 at 3:27 PM
Edited Feb 28, 2013 at 4:06 PM
So, feed back: So far I like it, not bad at all. I'm using the V8 engine obviously because I'm forcing an async framework, similar to NodeJS (but not the same modules, this is C# .NET/Mono focused).

You just added the attributes I needed this week, so I'm able to continue. I needed to lock down the public properties and methods so the engine could only see one's it should, and allow users of the C# library itself more complete control over the server engine. I'm also now able to follow the PROPER JavaScript naming convention, while also maintaining a proper convention within C#.

I wasn't sure why you had a property bag when C# already natively supports the ExpandoObject, but regardless, I needed a thread safe solution, so I created my own property bag collection. Why? Because I wrapped the V8 engine in a class that spawns a thread with a custom messaging queue. I wanted a way for the multiple V8 engine environments to share properties, so I created a root "globals" object property (via my custom thread-safe property bag) to allow global access to shared properties (so all a script has to do is "globals['MyVar'] = SomeValue;"). I'm also trying to implement native thread support via a "run" command (example: "run({varname:userdata}, threadedFunction, completedCallback);").

At this point I'm trying to decided whether to simply support the .NET types themselves, or wrap them in my own types (because, frankly,given my experience with developing in WPF and Silverlight [and .NET frameworks in general], very little seems to make sense until you study the quirks - probably because it tries to be all things to everyone, and with such complexity, it sports a bit of a learning curve).

Update: I think I'll implement my own server objects and just let the developer use your ExtendedHostFunctions object instead if they wish.
Feb 28, 2013 at 3:54 PM
Edited Feb 28, 2013 at 3:59 PM
I think this needs to be simplified:
var callback = host.del(TimerCallback, function (state) {
 Console.WriteLine('Timer fired: {0}.', state);
 }); 
var timer = new Timer(Timer, callback, 'foo', 5000, 5000);
It's doesn't feel natural. Something like this would be better if possible:
var callback = function (state) {
 Console.WriteLine('Timer fired: {0}.', state);
 };
var timer = new Timer(Timer, callback, 'foo', 5000, 5000);
I need it easy to use functions as callbacks from within the .NET environment. "Timer" might instead be one of my class objects that need to store a reference to a JavaScript function for call back.
Coordinator
Feb 28, 2013 at 6:17 PM
Edited Aug 3, 2013 at 3:33 AM
Hi jamesnw,

I think this needs to be simplified:

Yes, we've been looking for ways to improve how script code deals with delegates. The problem is that script functions don't have parameter and return type information, so they're difficult to map to the right delegate types for the purposes of method/constructor binding.

Therefore script code has to be explicit about the delegate type it's constructing. However, we believe that a syntax tweak might be possible:
var callback = new TimerCallback(function (state) { ... });
var timer = new Timer(callback, 'foo', 5000, 5000);
I wasn't sure why you had a property bag when C# already natively supports the ExpandoObject

We thought about using ExpandoObject, IDynamicMetaObjectProvider, or some other pre-existing type to trigger dynamic property support for script code. And we'd still like to do that. But we also wanted to give hosts the ability to easily customize dynamic property behavior. ExpandoObject just stores properties in memory, but we were looking for a more general mechanism that would be easy for hosts to customize. Unfortunately ExpandoObject is not extensible, and IDynamicMetaObjectProvider is painful to implement. That's why we came up with IPropertyBag.

I think I'll implement my own server objects and just let the developer use your ExtendedHostFunctions object instead if they wish.

Sure, just be aware that ExtendedHostFunctions allows script code to do just about anything, including importing entire assemblies worth of types :)
Feb 28, 2013 at 6:46 PM
Edited Feb 28, 2013 at 8:01 PM
Can't the parameters and return type be inferred by the delegate type of the parameter itself?

I have some methods that have parameters such as "(Action callback)" or "(Func<object,object> callback)", and so on. I'm sure it's possible to infer the parameters and return type by those delegates. :)

For that matter, you might even define special delegate support for "public delegate object JSFuncDelegate(params object[] args);", then let the host handle it. :)
Coordinator
Feb 28, 2013 at 7:41 PM
Edited Aug 3, 2013 at 3:33 AM
Hi jamesnw,

Can't the parameters and return type be inferred by the delegate type of the parameter itself?

To examine the parameter types we must first locate the method, and to do that we must first determine the argument types. That is, the argument types are (part of) the input to the method binding algorithm, but the type of a script function cannot be assigned until after binding. So it's a chicken-egg problem.

It's not impossible. We could look at every method overload and collect every possible delegate type, then retry binding with different combinations until it succeeds, but that gets really slow and extremely complicated when generic methods are in the picture.

For that matter, you might event define special delegate support for "public delegate object JSFuncDelegate(params object[] args);", then let the host handle it. :)

The host can already handle it. Remember that script functions are a special case of script objects, to which ClearScript provides easy access via dynamic:
// C#
public void CallMeBack(dynamic scriptCallback)
{
    scriptCallback(123, 456.789, "blah");
}
Feb 28, 2013 at 8:01 PM
Ah ok, I'll give that a try, thanks. :)
Feb 28, 2013 at 8:16 PM
Edited Feb 28, 2013 at 8:17 PM
FYI: I found this post here which explains what I meant:
http://stackoverflow.com/questions/429552/can-i-get-the-signature-of-a-c-sharp-delegate-by-its-type

You don't need to locate the method. You just need to explore the delegate type to get the parameters and return type.

For example, if I have a method "SomeMethod(Func<object,object> func)", then you will know that one object parameter is expected, and one object gets returned. Are you saying that it's not possible to call "func()" from the C# side, and invoke the assigned JavaScript function?

For instance, in script, I might write "SomeMethod(function(o){ /do something/ return o; });".

I think what you are trying to tell me is that, from the JavaScript side, IF there were overloads, which would you choose... well, I think this is not a show stopper. You simply support a SINGLE method scenario, and generate an ambiguity error if more than one exists. If there's only one method with a matching name, problem solved. :)
Coordinator
Feb 28, 2013 at 9:18 PM
Edited Feb 28, 2013 at 9:21 PM
Hi jamesnw,

You don't need to locate the method. You just need to explore the delegate type to get the parameters and return type.

Well, the delegate type is the type of a method parameter, so we do need to find the method, but you're right, that's easy if we're just searching by name.

*I think what you are trying to tell me is that, from the JavaScript side, IF there were overloads, which would you choose... *

Exactly.

You simply support a SINGLE method scenario, and generate an ambiguity error if more than one exists. If there's only one method with a matching name, problem solved. :)

Agreed. This is a good idea that could really streamline the simple case. If normal binding fails, we could use your technique as a fallback. That is, we use reflection and simple matching criteria to find a likely method - perhaps one with the right name and parameter count - and generate delegate wrappers of the right types for any arguments that are script functions.

Of course, this will not work for generic methods (e.g., SomeMethod<T>(Func<T, int> func)), and could bind to the wrong method unless we steer completely clear of overloads, but still, we'll definitely consider it! In the meantime, we'll implement the simplified delegate construction syntax above.

Thanks for your excellent input!
Feb 28, 2013 at 9:29 PM
I agree. JavaScript is simple, we don't need to support generic methods to complicate things. LOL. ;) Making it that easy to include JavaScript functions in parameters will most likely solve 98% of the use cases. :)
Feb 28, 2013 at 9:50 PM
Edited Feb 28, 2013 at 10:06 PM
Side note: When you do streamline it for the simple case (which handles most common situations) make sure to cache the binding result so you don't need to keep using reflection. ;)

Bottom line, I'm sure my (eventual) users ( ;) ) will be happy with a simplified methodology. :)
Coordinator
Feb 28, 2013 at 11:19 PM
Thanks again, jamesnw. And yes, ClearScript already has a bind cache. The full dynamic method binding process - the one that supports generic methods and everything else - is way more expensive than simple reflection :)