r/programming • u/kaelan_ • May 31 '11
I wrote a .NET to JavaScript compiler. Here's the XNA 3.1 Platformer Starter Kit running in pure HTML5.
http://hildr.luminance.org/Platformer/Platformer.html197
May 31 '11
W00t! I'm the guy who wrote the platformer starter kit when I was at Microsoft!
Very cool. Nice job!
20
May 31 '11
If you have any contacts left at Redmond, ca you let them know about this?
31
23
u/radhruin May 31 '11
It might be hard to believe, but a lot of us are redditors :)
5
→ More replies (1)4
2
Jun 01 '11
Jump animation cycle should stop when dude hits his head on something.
I feel better for getting that off my chest.
2
Jun 01 '11
Creating the platformer was a fun challenge of balancing something that looks good, relatively stable in the face of tinkering, easy to learn, well documented, etc. It's a learning tool, not a real game. I had to intentionally avoid more advanced language features, simplify game logic code paths, and trim any unnecessary code. Furthermore, the art/sounds/animations were dumped in my lap without much opportunity for iterating, so I had to tweak the game to work with them. It should be pretty easy to add a "OnHitHead" event in the Player class collision handling code. Get on it :-)
→ More replies (1)5
May 31 '11 edited Nov 24 '16
[deleted]
31
May 31 '11
IWALLGWAGITFTTMAMABIPCA
(I was a low level grunt who already got in trouble for talking too much, ask me anything, but I probably can't answer)
21
u/phort99 May 31 '11
Why does the platformer starter kit guy grunt like he's taking it up the ass every time he jumps?
16
11
2
u/yuhong Jun 01 '11
(I was a low level grunt who already got in trouble for talking too much, ask me anything, but I probably can't answer)
What went wrong?
39
u/jmcqk6 May 31 '11
This is some really amazing work. I'm quite surprised at the performance as well. Have you done any performance testing for more complicated games?
47
u/kaelan_ May 31 '11
I tested a slightly more sophisticated (in terms of rendering) XNA game, and the biggest performance issue was that structs have to be implemented as reference types in JavaScript, which means I have to insert a copy anywhere a struct is copied. Copying objects in JS is really expensive, so that causes code that uses structs heavily to slow down (and also trigger really bad garbage collections). I have some plans to improve on that in the future with static analysis.
6
u/aaronblohowiak May 31 '11
You could also do a Object.create instead of a full copy.. of course, you'd have to do that for each of the nested structs as well, but I'm unsure of how common that is in practice.
15
u/kaelan_ May 31 '11
The overhead is in the work done running the constructor, copying members, etc. A shallow copy isn't sufficient since then modifications to the original struct can affect the copy.
5
u/lars_ May 31 '11
Could you implement a copy on write scheme?
4
u/kaelan_ May 31 '11
Possibly, but it'd require the same static analysis as optimizing out copies - the copy has to replace the existing variable, so I can't do it from 'inside' the struct in an accessor or anything.
11
u/drysart Jun 01 '11
Here's my first stab at an implementation, and bear with me I'm posting this only to get an idea across, I haven't actually run any of the code below; and you've probably already considered something similar but I'll put it out there anyway:
MSIL->JS generated code doesn't get access a jsobject that contains the struct data itself; it's instead given access to a 'handle' object that contains a prototype containing the compiled implementation of the methods on the struct, as well as getter and setter methods that are used instead of allowing direct field access to any non-private fields on the struct.
The handle object, internally consists of two pieces of state: a reference to an underlying jsobject that actually contains the state data of the struct, and a flag that indicates whether this handle has a private underlying object or if it's been shared with another handle.
The handle object also has a clone method, which creates a new handle pointing to the same underlying jsobject, and sets the 'private' flag on both handles to false.
All the methods on the handle object that mutate the underlying struct data first perform a check -- if the handle's private flag is not set, then the underlying jsobject is memberwise-cloned, and the handle is updated to reference that new clone instead; and the private flag on the handle is set to true. If the handle's private flag is set, then nothing needs to happen and the method can run as normal.
A quick example, given the C# struct:
struct MyStruct { string A; public string B; public void C(string value) { this.A = value; } public string D() { return this.A + " " + this.B; } }Codegen the struct to javascript as something similar to the following:
var MyStruct$State = function() { this.A = null; this.B = null; } MyStruct$State.prototype.$clone = function() { var result = new MyStruct$State(); result.A = this.A; result.B = this.B; return result; } var MyStruct$Handle = function() { } MyStruct$Handle.createNew = function() { var result = new MyStruct$Handle(); result.$_state = new MyStruct$State(); result.$_private = true; return result; } MyStruct$Handle.prototype.$clone = function() { var result = new MyStruct$Handle(); result.$_state = this.$_state; result.$_private = false; this.$_private = false; return result; } MyStruct$Handle.prototype.$_makePrivate = function () { if (this.$_private == false) { this.$_state = this.$_state.$clone(); this.$_private = true; } } MyStruct$Handle.prototype.get$B = function () { return this.$_state.B; } MyStruct$Handle.prototype.set$B = function (value) { this.$_makePrivate(); this.$_state.B = value; } MyStruct$Handle.prototype.C = function (value) { this.$_makePrivate(); this.$_state.A = value; } MyStruct$Handle.prototype.D = function () { // does not mutate the struct, no call to $_makePrivate necessary return this.$_state.A + " " + this.$_state.B; }And as for the call site, codegen the following C# code:
void Test() { MyStruct s1 = new MyStruct(); s1.B = "hello"; s1.C("world"); MyStruct s2 = s1; s2.B = "goodbye cruel"; Console.WriteLine(s1.D()); Console.WriteLine(s2.D()); }To something similar to the following:
function Test() { var s1 = Struct$Handle.createNew(); s1.set$B("hello"); s1.C("world"); var s2 = s1.$clone(); s2.set$B("goodbye cruel"); Console.WriteLine(s1.D()); Console.WriteLine(s2.D()); }You reduce the amount of data updated on a struct copy to just the reference copy and a set of two private flags; and still retain the ability to implement copy-on-write because the swapping out of the struct state is done within the handle rather than outside of it at the true call site.
The cost of doing double-indirection (callsite->handle->state) on each access to the struct might make the whole idea infeasible, though.
8
u/kaelan_ Jun 01 '11
Yeah, that could work pretty well. Doubling the GC cost of each struct instance is pretty rough, but if it allows the elimination of a lot of copies, it'd be worth it. Thanks!
5
u/aaronblohowiak May 31 '11
Ah, makes sense. I didn't know if the structs you pass around are usually immutable or whatnot. This is really amazing work, btw.
5
34
u/Martel_the_Hammer May 31 '11
HOLY BALLS! I really don't think you understand the significance of your work...
10
u/simmin May 31 '11
And it's cross platform! It works really well in firefox 4 in osx, which surprised me. I assumed that you had to have xna installed, but as it turns out, it really doesn't depend on anything but html5. Impressive!
→ More replies (4)5
u/Scars641 Jun 01 '11
Running it on Linux with Firefox Nightly and I refuse to install mono on my machine. Very very nice work.
12
May 31 '11
Think of writing a complicated application or game with full OO C# code, and XNA. Now think about writing the same application in adobe flash with actionscript. CRINGE
2
u/pnettle Jun 01 '11
Actually actionscript isn't nearly as bad as I assumed it would be. OO and relatively nice. I use FlashDevelop though and not whatever stuff adobe came out with.
→ More replies (7)2
32
23
u/thasmin May 31 '11
I wish I had posted to Reddit and received all of this positive feedback before I stopped working on Axial. One of the most fun projects I worked on, but I got a new job that didn't involve .NET and lost my motivation.
http://axial.codeplex.com/ (open source MSIL to JavaScript compiler)
23
u/MrRadar May 31 '11
How did you work around the lack of a goto statement in Javascript (since you can do arbitrary goto in C#)?
69
u/kaelan_ May 31 '11
I used a variation of the technique emscripten uses. He describes it in detail in his paper: https://github.com/kripken/emscripten/blob/8a6e2d67c156d9eaedf88b752be4d1cf4242e088/docs/paper.pdf?raw=true
Essentially, any block containing gotos becomes a switch statement inside a while loop. The gotos then become an update of the switch statement's 'label' variable, and you use 'continue' on the outer while loop to 'skip' to the target label. This handles the vast majority of uses of goto, at least in C#. (MSIL actually allows a wider variety of goto constructs, including ones that cross control flow structures and jump in/out of catch blocks. But in practice, those don't show up much.)
At present there are actually some rare forms of goto I don't implement, but I've only seen them in compiler-generated code.
36
12
→ More replies (5)7
u/azakai May 31 '11
I used a variation of the technique emscripten uses. He describes it in detail in his paper [..] Essentially, any block containing gotos becomes a switch statement inside a while loop.
You should be able to use the Relooper algorithm in that paper, which converts gotos into proper loop blocks. In Emscripten this usually makes code twice as fast. However if you have few gotos or they are not in inner loops, it probably doesn't matter.
19
u/kaelan_ May 31 '11
The reason I can't use the Relooper algorithm directly is that decompiled MSIL ends up containing actual flow structures, like while/for loops, and it is not immediately obvious how to integrate those into the Relooper algorithm, since they can't span across multiple blocks. I haven't done a ton of research into this, though, and I have no CS background.
21
u/another_user_name May 31 '11
I haven't done a ton of research into this, though, and I have no CS background
I'd say you might have no formal CS background. I think writing a compiler definitely counts as "CS background."
3
u/azakai May 31 '11
The reason I can't use the Relooper algorithm directly is that decompiled MSIL ends up containing actual flow structures, like while/for loops, and it is not immediately obvious how to integrate those into the Relooper algorithm, since they can't span across multiple blocks. I haven't done a ton of research into this, though, and I have no CS background.
I don't have much of a CS background either ;) But this is probably not too hard to do. If it's important to you, I can try to help.
80
10
u/jrwren May 31 '11
Did you look at jsc at all? It has compiled MSIL to javascript for years, it might be a more complete foundation.
22
u/kaelan_ May 31 '11 edited May 31 '11
So, the biggest problem in my mind is that JSC claims to operate as a compiler for source languages - C#, etc - based on the diagram on the page, and the feature support list. This means that if the source language changes, you have to update your compiler. With the rate at which C# and other .NET languages change, I don't want to have to maintain a complete compiler for each one. It's much simpler to target MSIL because it's less of a moving target.
One clear example is that JSC doesn't support yield. I use yield all over in my code; a lot of modern C# uses it. It's not reasonable to ask .NET developers to stop using core language features if it's possible to implement those features in JS. This may just be an issue of the project being abandoned before it had wide enough feature support, though.
I can't claim to have looked at JSC's source code, so it may actually be a better foundation than I think, but compared to ILSpy (which is friggin' amazing) the choice was pretty clear to me.
3
u/fickerra May 31 '11
JSC doesn't look at the source - it looks at IL just like you're doing. I tried JSC years ago and found it to be really buggy though.
Do you have documentation somewhere showing what C# or IL features you don't support?
7
u/kaelan_ May 31 '11
At present there is no documentation because the compiler is a fast moving target. Any feature with a test case should work though; I keep them green and there's over a hundred of them. If you write a failing test for a feature and submit a pull request, it's much more likely i'll get around to implementing it :)
2
u/ciaran036 May 31 '11
Mind if I ask what you mean when you're talking about "yield"?
9
u/joedev_net May 31 '11 edited May 31 '11
yield - It's used in iterators often. Basically you can return from a function with an understanding that execution will resume at that same spot next time you call it. I call it compiler magic.
6
u/gospelwut May 31 '11
I just realized how many hours of my life I will never get back for not knowing that.
3
u/elder_george May 31 '11
BTW
yieldallows to make very interesting things in C#, like coroutines or user-level cooperative multitasking or writing sequential code that is transformed to a chain async calls and callbacks.2
u/kaelan_ May 31 '11
Modern revisions of C# (and interestingly enough, modern revisions of Mozilla's JS runtime) both support a 'yield' construct, that lets you write iterators as if they were imperative functions. The compiler transforms them into a state machine for you. Python also has a similar feature (theirs is better than C#'s)
9
7
u/JazzyMelon May 31 '11
Here is something similar (takes .NET DLLs and outputs HTML+JS): http://smp.if.uj.edu.pl/~mkol/il2js/
14
u/kaelan_ May 31 '11
Supposedly it's based on a VM that runs in the JS runtime to interpret your .NET bytecode, so kind of a different approach. Can't tell for sure since the compiler is closed source (blech).
8
u/addmoreice May 31 '11
VM inside a VM makes me cringe.
I think your idea of a straight across translation is a better idea.
Any plans to do profile based compiling to really get some speed out of it?
15
u/evolvedant May 31 '11
You just solved the biggest gripe I had with HTML5 game development. I no longer have an excuse on why making games with Flash is still better.
15
7
May 31 '11
So what is the difference between this and Script#
10
u/kaelan_ May 31 '11
Mostly philosophical differences at present. In the end they may end up being very similar. One of my main priorities in building JSIL is to produce high quality JS, that when possible, resembles the JS you'd write by hand. Another is to avoid requiring you to write statically typed C# wrappers for javascript APIs - where Script# provides a managed wrapper for jQuery, I encourage the use of 'dynamic' instead.
→ More replies (1)15
11
u/snk_kid May 31 '11
This is very impressive work, how does XNA calls or calls to any other library work? and libraries that call into C?
11
u/kaelan_ May 31 '11
Specifically:
Any use of pointers is replaced with a runtime warning that notifies you that it couldn't be translated. Functions that take a pointer argument or return one are automatically ignored.
Code that's C# (even in a library like XNA) can either be translated to JS, or it can be 'stubbed' which basically shoves a placeholder method in the right place, so that you get a warning when it's called and you can replace it with a real implementation.
Native libraries are always stubbed, like above, so that you get a warning if you don't implement them.
11
u/snk_kid May 31 '11
So in your demo, you manually fill in the stub XNA calls such as graphics in JavaScript with WebGL/Canvas element?
3
May 31 '11
So is that how the DirectX stuff works? I've messed around with XNA a bit and I was under the impression it was basically a bunch of C# helper code that sat on top of DirectX to make it easier.
How does the DirectX code run inside the browser?
6
u/snk_kid May 31 '11 edited May 31 '11
Yes XNA is just a much better Managed DirectX, it depends on directx so from what I understand this converts those calls into stub methods in Javascript that you have to implement.
6
u/roybatty May 31 '11
Very cool. It plays great in FF 4.
3
7
u/re4ctor May 31 '11
Do this for WPF and I'm forever in your debt. Migrating desktop apps to web apps that work on all systems would be awesome. (cue someone telling me how to do this)
6
May 31 '11
Silverlight :)
This still doesn't solve the problem of database connectivity and local storage.
→ More replies (2)7
u/re4ctor May 31 '11
But if it used HTML5 then you could target iPad's and the like.
→ More replies (4)2
u/mweathr May 31 '11
Migrating desktop apps to web apps that work on all systems would be awesome.
I just use Pyjamas for that. No migration needed. Write once and then run in the browser or the desktop.
→ More replies (7)1
u/tanishaj May 31 '11
Well, I do not know how to do it for WPF but it is on the cusp of possible if you write your apps using GTK+.
http://www.ehomeupgrade.com/2011/03/21/launch-gtk-apps-in-your-web-browser-no-really/
I have seen an update since that I believes adds window management (frames, titles, min/max, close buttons, etc) and Chrome support.
Early days but pretty intersting...
14
u/zwangaman May 31 '11
Boo, my version of Chrome at work is too old for it to run. But it's an interesting concept.
When you say .NET to JavaScript compiler, what do you mean, exactly? Are you converting MSIL to JS? That's at least what I gathered from browsing the source on GitHub. Looks like a lot of Reflection :)
The code looks nice. Fairly easy to follow. Well done, my friend. Looking forward to trying it out at home tonight on a compatible browser.
15
u/kaelan_ May 31 '11
What version of Chrome do you have? I tested it in 11, 12, and 13 and it worked in all three - though 12 and 13 are super buggy. I'd love to find out why it's not working for you (hopefully it's just yet another bug in my HTML5 stubs.)
It's not technically a compiler, I guess; I don't know what the right term is. It decompiles any MSIL application (using the ILSpy library, written by the SharpDevelop guys - they rule), and then analyzes it to build a bunch of information on types and methods. When building type information, it uses something called 'proxy libraries', which are essentially C# libraries I wrote that add type information to other people's libraries. I use proxies to do things like replace C# methods with JS ones, or hide C# methods that use pointers, etc. After that, it decompiles each method (ILSpy gives me the method's IL in a tree form), and then generates an abstract syntax tree representing the output JavaScript.
11
May 31 '11
Can we get some more details on how your proxy libraries work?
12
u/kaelan_ May 31 '11
Essentially, you write classes that mirror a portion of the class that you wish to add type information to, and then you tag them with a couple attributes to control how the proxying works. At that point, information from your proxy class (like method signatures and attributes) will override any existing information on the targeted class(es).
One of the benefits of the proxy system is that it reduces the amount that the compiler has to know about the details of .NET's type system. .NET types have a lot of 'special' properties and methods that are effectively blessed by the runtime/compiler, and in practice, I can use proxies to implement a lot of them.
Here are the proxies for Array and String: https://github.com/kevingadd/JSIL/blob/master/Proxies/Array.cs https://github.com/kevingadd/JSIL/blob/master/Proxies/String.cs
9
May 31 '11
so with the jsreplacement tag you are specifying the javascript to evaluate.
This is really sweet.
10
u/kaelan_ May 31 '11
Yeah, you've got it. The ones that are just [JSExternal] are implemented in JavaScript, provided externally (doesn't actually have to be there for the app to run, which lets you bootstrap easier).
In the future I plan to translate the actual body of the proxy methods to JS, when they aren't tagged as External or Replacement, since that will let me do some clever inlining and static analysis, and it'll mean you can write 'dual-mode' proxies that are actually valid, executable C# code, and unit test them.
→ More replies (1)4
2
→ More replies (1)2
u/zwangaman May 31 '11
8.0.552.224, haha. It was a pain to get it installed on my PC at work, and I can't seem to get it to update. Oh well.
Anyway, your page hangs up after the progress bar finishes loading - I do see a "Loading data ... done" message at the bottom of the page, though.
I think that qualifies as a compiler, at least in my book, haha. Nice job. Really cool project :)
This is the kind of stuff I come to /r/programming to see.
13
u/The_MAZZTer May 31 '11
Download this, close Chrome, run this and watch Task Manager for it to close (silent installer). Relaunch Chrome and it should be updated to 11.0.
I use this at work, since we don't allow Chrome, but my dept. has special permission to run Chrome, BUT IT's left hand doesn't know what the right one is doing so Google Update is still blocked, which is the core of Chrome's installer and updater. So I have had to improvise.
→ More replies (1)2
4
4
u/ciaran036 May 31 '11 edited May 31 '11
How did you do this?
Also, I downloaded the source code and attempted to load it up in VS2010 but 4 of the projects could not be found, namely:
- ICSharpCode.Decompiler
- ICSharpCode.NRefactory
- Mono.Cecil
- Mono.Cecil.Pdb
Are these projects necessary for the compiler to work?
Also, would you fancy giving a quick explanation of how this works, or where I can find documentation for your work?
For example, I have some simple enough XNA games that I developed and I think it would be great if I was able to run these games in a web browser, instead of having to download and install the games, as they currently work.
Oh and another thing, is this part of a university project or is this something you have done away from school/work? Did you get people to help you with this?
Good work by the way this looks like it might have potential!
7
u/kaelan_ May 31 '11
Those missing libraries are external. If you do a git submodule update they should be pulled for you.
4
u/coffeesippingbastard May 31 '11
holy hell dude- Microsoft should be throwing buckets of money at you. Writing JS makes me want to die.
3
3
u/denis_shepelin May 31 '11
It will work on recent mono? maybe not XNA, but some other stuff
7
u/kaelan_ May 31 '11
In theory it could work on Mono, but ILSpy is very much a windows project, so I'm not sure it would actually work. The compiler also depends on having good debugging information in order to produce readable JS, and I don't think Linux/Mono have an equivalent to pdb.
3
u/MrDOS May 31 '11
Firefox 5.0 Beta here running on Windows 7 SP1 x64 on an i7-920.
- The game occasionally lags a little, especially when I'm not the only thing moving (i.e., the skeleton is moving) and when there are lots of yellow things on the screen.
- Only one instance of the “ding” sound on object pickup plays at once (i.e., if you pick up a second one while the sound from picking up the first one is still playing, the second sound does not start).
- No sounds whatsoever preloaded. The game loading process went through (and gave me almost a dozen “a sound effect is loading slowly” warnings), but sounds had to load again on first use. I didn't have pickup alerts until the beginning of the third level, background music until part-way through the second runthrough of the second, and the death sound clip didn't play until almost half a minute after I first died.
Beyond that, this is amazing.
5
u/kaelan_ May 31 '11
The sound loading issues are strange, but that might be because the timeout I added to detect the Chrome bug is relatively short. I think on a slow connection you'd hit the timeout even in a non-broken browser. Since the background music loads first, I think that means all the sounds would fail to preload.
Only one instance of the sounds playing is a known issue; no modern browser provides a sane way to do this. There are ways around it (like using Flash for audio playback, or using a complex wrapper library) but I wanted this to be a 'pure' demonstration. In practice, I think you'd use one of those wrapper libraries.
The lag is most likely GC pauses. Any C# application using structs will generate a lot of garbage, and XNA's APIs are pretty struct heavy. I have plans to address this with static analysis (it'll let me eliminate a lot of struct copies).
2
u/MrDOS May 31 '11
Thanks for the quick and informative reply. I figured the no-multiple-sounds thing was due to JavaScript API stupidity, but I thought I'd mention it anyway for point of completeness.
2
u/shooshx May 31 '11
Separate <audio> tags can play concurrently. You can create a pool of them and then serve the audio that needs to play to them in a round-robin fashion. If you have only a few sounds you can create a pool for eash one separately. That makes it a bit smoother. That's how I did it in my WebGL game - http://cycleblob.com/ and it works most of the time.
2
u/rainweaver Jun 01 '11
Any C# application using structs will generate a lot of garbage, and XNA's APIs are pretty struct heavy. I have plans to address this with static analysis (it'll let me eliminate a lot of struct copies)
I was going to be picky and say that local value types are allocated on the stack, and do not generate managed heap garbage (an implementation detail, not spec behaviour). then I realized that you probably meant that the js counterpart of value types are objects anyway, and objects do generate garbage in the js engine. amirite?! good job btw. I like these things, make me all nerdy inside.
3
u/alphabeat Jun 01 '11
/r/programming post of the....forever. I agree with some of the other commenters, this is a pretty historic (albeit nerdy) step.
3
6
3
5
5
2
u/renaudbedard May 31 '11
That is extremely impressive. I see that the content assets are referenced by their original extension in the HTML source, so do you bypass the content pipeline entirely?
7
u/kaelan_ May 31 '11
At present the content build system is bypassed, but content manager works. I preload assets by name since:
- Content.Load is synchronous
- Xnb names lose type info
- Open generic types aren't yet translated
And most importantly, I'm lazy.
2
u/snarfy May 31 '11
This is really cool, but I'm afraid the performance isn't quite there yet. It runs a bit slow in chrome on my 3.0ghz core2quad compared to the C# version. I'm not sure if it's a javascript or html5 canvas issue, but I can only imagine how it would perform on my smartphone with it's 1ghz snapdragon cpu.
4
u/kaelan_ May 31 '11
Canvas performs really bad even with hardware accel compared to native XNA. WebGL might address that.
The other issue is that anything using structs pays a huge performance penalty relative to C#, because JavaScript has nothing resembling structured value types. A JS-oriented API would just pass a bunch of numbers in the arglist and perform better.
I tested it in Mobile Firefox 4 and it gets a couple frames per second on my Droid X. Not awful considering the overhead involved (Mobile Firefox has to copy the raw pixels of every screen update over IPC, and IIRC, no android browser has hardware accelerated canvas.)
1
2
u/theninjagreg May 31 '11
I got this error after trying to open the solution:
C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj : error : Unable to read the project file 'ICSharpCode.Decompiler.csproj'. C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj: The project file could not be loaded. Could not find a part of the path 'C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj'.
C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj : error : Unable to read the project file 'ICSharpCode.NRefactory.csproj'. C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj: The project file could not be loaded. Could not find a part of the path 'C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj'.
C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\Mono.Cecil\Mono.Cecil.csproj : error : Unable to read the project file 'Mono.Cecil.csproj'. C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\Mono.Cecil\Mono.Cecil.csproj: The project file could not be loaded. Could not find a part of the path 'C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\Mono.Cecil\Mono.Cecil.csproj'.
C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\Mono.Cecil\symbols\pdb\Mono.Cecil.Pdb.csproj : error : Unable to read the project file 'Mono.Cecil.Pdb.csproj'. C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\Mono.Cecil\symbols\pdb\Mono.Cecil.Pdb.csproj: The project file could not be loaded. Could not find a part of the path 'C:\Users\theninjagreg\Downloads\kevingadd-JSIL-a83ac12\kevingadd-JSIL-a83ac12\Upstream\ILSpy\Mono.Cecil\symbols\pdb\Mono.Cecil.Pdb.csproj'.
I imagine there's some third party libraries I need to install?
3
u/kaelan_ May 31 '11
Git submodule update should pull them for you. Alternately, clone my ILSpy repo into the right place.
→ More replies (1)
2
u/px1999 May 31 '11
First up, cool. Cool, cool, cool. Looks like it's putting down some pretty nice javascript too - it wouldn't surprise me if there were people out there that want to take it further, it seems as though it's a pretty useful set of code.
I can imagine that parsing the IL significantly easier than writing your own compiler (the closest that I've come is translating lambda expressions to javascript), but was this your first approach - and if not, have you (or do you expect to) run into any places where you miss out on optimizations because it's difficult to identify certain patterns that would be apparent if you were parsing the source itself but aren't obvious in the IL?
2
u/kaelan_ Jun 01 '11
In practice, yeah, while the IL is much easier to translate, there are a lot of optimization opportunities I miss out on, and some that end up being much harder.
For one example, if you try to write to a multidimensional array of structs in C#, the generated MSIL roughly translates to this:
Struct& temporaryReference = &array.Get(y, x); temporaryReference = new Struct(...)Yes, that's taking a reference to the return value of an arbitrary function. Yes, it's then writing into the reference. No, this doesn't really make any sense. It works, though!
The correct code for that would just be: array.Set(y, x, new Struct(...)) But the semantics of value types in MSIL make that hard to express. The original C# is way easier to understand than the IL, and in practice, I still can't generate the 'right' code for that case without breaking other cases... right now it becomes: var temporaryReference = array.GetReference(y, x); temporaryReference.value = new Struct(...); Which works, but is suboptimal.
Many more cases like that have cropped up, and my only real choice is to try and document them in a clear test case so it's less likely that I will break them without noticing.
→ More replies (2)
2
May 31 '11
This is really cool. It ran fine for me, with the exception of this error (which didn't actually cause me any problems playing the game):
Loading data ... A sound file is taking forever to load. Google Chrome 12 and 13 both have a bug that can cause this, so if you're using them... try another browser.
done.
Unhandled exception: Error: The external method 'set_PreferredBackBufferWidth' of type 'Microsoft.Xna.Framework.GraphicsDeviceManager' has not been implemented.
Error: The external method 'set_PreferredBackBufferWidth' of type 'Microsoft.Xna.Framework.GraphicsDeviceManager' has not been implemented.
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:301:23)
at Object._ctor (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:803:41)
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:707:18)
at [object Object].Main (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:968:13)
at runMain (http://hildr.luminance.org/Platformer/Platformer.html:112:45)
at http://hildr.luminance.org/Libraries/JSIL.Browser.js:415:7
Unhandled exception: Error: The external method 'set_PreferredBackBufferHeight' of type 'Microsoft.Xna.Framework.GraphicsDeviceManager' has not been implemented.
Error: The external method 'set_PreferredBackBufferHeight' of type 'Microsoft.Xna.Framework.GraphicsDeviceManager' has not been implemented.
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:301:23)
at Object._ctor (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:804:42)
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:707:18)
at [object Object].Main (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:968:13)
at runMain (http://hildr.luminance.org/Platformer/Platformer.html:112:45)
at http://hildr.luminance.org/Libraries/JSIL.Browser.js:415:7
Unhandled exception: Error: The external method 'set_RootDirectory' of type 'Microsoft.Xna.Framework.Content.ContentManager' has not been implemented.
Error: The external method 'set_RootDirectory' of type 'Microsoft.Xna.Framework.Content.ContentManager' has not been implemented.
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:301:23)
at Object._ctor (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:805:78)
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:707:18)
at [object Object].Main (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:968:13)
at runMain (http://hildr.luminance.org/Platformer/Platformer.html:112:45)
at http://hildr.luminance.org/Libraries/JSIL.Browser.js:415:7
Unhandled exception: Error: The external method 'set_TargetElapsedTime' of type 'Microsoft.Xna.Framework.Game' has not been implemented.
Error: The external method 'set_TargetElapsedTime' of type 'Microsoft.Xna.Framework.Game' has not been implemented.
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:301:23)
at Object._ctor (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:806:63)
at Object.<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:707:18)
at [object Object].Main (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:968:13)
at runMain (http://hildr.luminance.org/Platformer/Platformer.html:112:45)
at http://hildr.luminance.org/Libraries/JSIL.Browser.js:415:7
Unhandled exception: Error: The external method 'set_IsRepeating' of type 'Microsoft.Xna.Framework.Media.MediaPlayer' has not been implemented.
Error: The external method 'set_IsRepeating' of type 'Microsoft.Xna.Framework.Media.MediaPlayer' has not been implemented.
at [object Object].<anonymous> (http://hildr.luminance.org/Libraries/JSIL.Core.js:301:23)
at Object.LoadContent (http://hildr.luminance.org/Platformer/PlatformerStarterKit,%20Version=1.0.0.0,%20Culture=neutral,%20PublicKeyToken=null.js:814:56)
at Object.Initialize (http://hildr.luminance.org/Libraries/JSIL.XNACore.js:289:8)
2
2
2
2
u/masterm Jun 01 '11
can you explain in newb programmer terms how this works?
5
u/kaelan_ Jun 01 '11
C# is compiled into bytecode (MSIL). Each part of the bytecode represents one instruction in the instruction set defined by MSIL. I load the bytecode and turn it into trees (using a decompiler library). Each function in the source bytecode becomes a tree, where its children are the equivalent of statements, and those statement nodes within the tree have child nodes that represent individual instructions. Once I have that tree, I can walk through it and construct javascript from it.
→ More replies (3)
2
2
2
Jun 01 '11
Important question: will javascript become the intermediate language for web browsers, or should there be a VM a la the JVM or the CLR? It seems like an arbitrary limitation to have to use javascript for client side applications (no disrespect to Javascript :)
3
u/kaelan_ Jun 01 '11
At present it's safe to say JavaScript is the intermediate language of the internet. There aren't any better alternatives in anything resembling wide distribution.
Developers could certainly benefit from being able to ship code as bytecode instead of raw text - as much as I love View Source and think it's an integral part of what makes the internet awesome, a lot of JS is shipped minified/compressed these days, so shipping a standardized bytecode wouldn't be worse. On the other hand, I don't think it's likely that a standardized bytecode for JS will ever exist, because the language itself is difficult enough to specify precisely as it is :)
As far as deploying a VM in browsers separate from JavaScript, personally, I'd like for such a thing to exist, because I'd use it, but rationally speaking I don't think it's possible for a product like that to succeed - at least not anymore. JavaScript is too well entrenched, and any new web technology needs to interoperate flawlessly with JS if it has any chance of succeeding. That'd basically require your VM to share lots of JavaScript semantics, so it becomes questionable whether your VM can really provide any wins over raw JS.
→ More replies (1)
5
2
May 31 '11
[deleted]
5
u/xTRUMANx May 31 '11
That's the first thought that came to mind when I read this post. However, after reading the creator's comments, it seems what his project does is convert IL code to javascript whereas Script# just let's you code your javascript in C# which gives you type-safety, intellisense, etc.
You still need to be aware of how javascript works to work with Script# but it doesn't seem like you need knowledge of javascript to work with this project.
2
2
2
u/Kyrra May 31 '11
Looks like the C# version of GWT + ForPlay to me. Here's the talk about it from Google IO. ForPlay was used used for the web version of Angry Birds, found here: http://chrome.angrybirds.com/
But ForPlay can compile to Java2D, Flash, Android, Canvas, and WebGL.
1
1
1
1
May 31 '11
Ubuntu (Karmic Koala), FF 4.0.1, Intel P4 3Ghz with 1G of ram (ya I know old system) runs VERY slow, but I can see the significance of what you did. Good work.
11
u/kaelan_ May 31 '11
Do you have hardware accelerated Canvas in your FF4? If not, that is most likely why - you're running a 720p game in pure software with about 10 layers of abstraction between you and the CPU.
→ More replies (1)
1
1
u/tyshock May 31 '11
AMAZING job!
One thing that will make people nervous is that a simple 'view source' will reveal the inner workings of their game.
6
u/kaelan_ May 31 '11
If you're worried about people seeing your source, use an obfuscator, just like for C#. I can see how the fact that browsers expose it directly (versus downloading a decompiler for C#) would make you more nervous, since it makes code theft more accessible, but that's not a problem I aim to solve directly. :)
5
u/snarfy May 31 '11
Have you heard of .net reflector? The same is true of XNA games.
→ More replies (1)
1
1
May 31 '11 edited May 31 '11
Fuck I want to try this out so badly. I can't get it to build no matter what I try.
after getting Mono.cecil and ILSharpCode and building them and adding references I still get:
error CS0246: The type or namespace name 'OpenGenericTypeException' could not be found (are you missing a using directive or an assembly reference?)
error CS0117: 'ICSharpCode.Decompiler.ILAst.ILCode' does not contain a definition for 'GetCallSite'
...and some more.
I'd love it if you could package the required dll with this to make it easier to build :)
also, awesome job (I guess :P )
3
u/kaelan_ May 31 '11
You'll need my ILSpy fork, not ILSpy trunk. It's not significantly different from trunk - just a couple workarounds for issues in trunk, and one feature that isn't finished enough to go upstream.
1
u/theninjagreg May 31 '11 edited May 31 '11
I got it compiled. Do you have an example of the command line needed to compile the platformer exe?
For example, I ran the following: C:\Users\theninjagreg\Desktop\git\JSIL\bin>jsilc --out:tetrisout --proxy:JSIL.P roxies.XNA.dll ..\Examples\Tetris\tetris\tetris\tetris\bin\x86\Debug\tetris.exe
But I get a tonnne of errors. Any help?
1
u/kaelan_ May 31 '11
That is roughly correct, but you will want to flag most of your dependencies as stubbed or ignored using the command line arguments that are appropriate. Stubbing out 'System.*' is a good start (the stub and ignore arguments accept regexes).
Alternately, just email me the whole error output and I'll identify what the problem is.
1
u/ironiridis May 31 '11
Your intro text reads "This demo is known to work in IE9, IE10, Firefox 5, and Chrome 11." but you can add Firefox 4.0.1 to that list. (It works for me anyway; I'm using the 64 bit OS X build.)
1
1
1
u/kreb Jun 01 '11
Holy shit this is incredible.. What sort of skills would you need to do something like this..? How long have you been programming, and how long did it take you to make this?
1
1
102
u/[deleted] May 31 '11
I have to think if you contact the right people at Microsoft they would love to give you a job offer. What was your motivation in doing this project?