C# kód futtatása Node.js környezetben Roslyn segítségével

Amikor a Microsoft Managed Languages csapata 2013. decemberében bejelentette, hogy a korábbi C# és Visual Basic fordítót lecserélték, és már a Visual Studio következő verziójának napi buildjei is egy új fordítóval készülnek, hirtelen mindenki számára egyértelművé vált, hogy valami nagyon komoly dolog van készülőben. Az új, “Roslyn” kódnevű eszköz messze túlmutat a korábbi csc.exe és vbc.exe képességein és lehetőségein, nem véletlenül kapta végül a .NET Compiler Platform elnevezést.

Itt ugyanis nem egyszerűen arról van szó, hogy szeretnénk a forráskódunkat futtatható formára alakítani, hiszen arra már sok éve volt kiváló eszközünk. A Roslyn célja, hogy a fordító tudását API-kon keresztül megnyissa a fejlesztők és a fejlesztőkörnyezetek (például a Visual Studio) előtt, azaz egy fordító-mint-szolgáltatás (compiler-as-a-service) megoldásról beszélhetünk.

Miért van erre szükség? Leginkább azért, mert a fordítók nagyon összetett szerkezetek, és futásuk közben igen komoly mennyiségű információval rendelkeznek a forráskódunkról:

compiler-architecture

Ezt a tudást vétek bezárni egyetlen eszközbe, sokkal jobban járunk, ha a fordító által felhalmozott információkból más eszközök is mazsolázhatnak. A fordító az egyetlen eszközünk, amely valóban érti a forráskódunkat, hiszen a kód minden egyes betűjéről tudja, hogy az utasítás, adat, komment stb. Ezt felhasználva sokkal jobb fejlesztőeszközöket készíthetünk, például a Visual Studio 2015 kódszerkesztőjében számos funkció a Roslyn nélkül valószínűleg soha nem valósulhatott volna meg.

Az alábbi ábra a Roslyn platform felépítését mutatja (katt a teljes képért):

roslyn-architecture

Aki szeretne minden egyes dobozt megérteni, bátran látogasson el a projekt honlapjára, én itt most csak annyit emelnék ki, hogy a forráskód olvasásától az értelmezésén keresztül egészen a futtatható kód generálásáig megtalálható itt minden, és mivel platformról van szó, természetesen mindenhez van API.

A kulcs elem a sárgával bekeretezett Syntax Tree, ami a Parser által létrehozott belső reprezentációja a teljes forráskódunknak. Az ehhez tartozó API-t hívják Roslyn Syntax API-nak, aminek segítségével nem csak forráskódból értelmezhetjük a forráskódunkat, hanem módosíthatjuk is azt. Példaként vegyük az alábbi utasítást:

Regex.Match("my text", @"\pXXX");

Ebből a Roslyn az alábbi szintakszisfát építi fel:

Turner_Figure4_hires

(A példát az MSDN Magazine Use Roslyn to Write a Live Code Analyzer for Your API című cikkéből kölcsönöztem, amiben egy Visual Studio bővítményt készítenek erre alapozva.)

Hogy mi ezt a fát pusztán csak nézegetjük, elemezzük, vagy módosítjuk is, az már kizárólag csak rajtunk múlik.

Felismerte ezt a TypeScript csapat is, és az 1.3 verziótól kezdve már a Roslyn motor szolgáltatja Visual Studioban a kódszerkesztő funkciókhoz a szükséges adatokat. Ennek köszönhetően a TypeScript fordító architektúrája nem csak hogy jelentősen letisztult, hanem egyúttal könnyebben érthetővé is vált:

typescript-architecture

Történetünk szempontjából az egyik fontos komponens az alsó dobozban szereplő Parser, a másik pedig az Emitter. A Parser feladata, hogy felépítse az absztrakt szintakszisfát – jelen esetben a TypeScript forráskódból –, az Emitter feladata pedig, hogy a szintakszisfa alapján elkészítse a fordító kimenetét – jelen esetben JavaScript (.js), definíció (.d.ts) vagy source map (.js.map) formában.

Érdemes észrevenni, hogy a Roslyn és a TypeScript architektúrája teljesen hasonló: a forráskódból előbb fát építünk, majd a fából más nyelvű kódot generálunk. A Roslyn esetén ez C# –> fa –> IL, a TypeScript esetén egy TypeScript –> fa –> JavaScript.

Mivel a két fa azonos, összekapcsolhatjuk a kettőt és így máris eljutunk az alábbi megoldáshoz:

flow

Azaz a Roslyn és a TypeScript felhasználásával képesek lehetünk C# forráskódot JavaScriptre fordítani!

A módszer előnye, hogy mivel közvetlenül a C# forráskódból indulunk ki (ellentétben például a JSIL projekttel, ahol Common Intermediate Language kódot fordítanak JavaScriptre), minden olyan információ rendelkezésünkre áll, ami a forráskódban még elérhető (például láthatóság, öröklés stb.), de egyébként a C# fordító kimenetén már nem jelenik meg. Mindez meglepően hatékony optimalizálási lehetőségeket nyújt!

Példaként vegyük a JSIL projekt oldalán elérhető Raytracer demót. A teljes C# forráskód 429 sor, a JSIL által készített JavaScript kód 793 sor. Mondhatnánk,  hogy örülhetünk, hogy egyáltalán fut, csakhogy a memória kezelése korántsem optimális:

raytracer-memory

Ugyanezt a forráskódot a Roslyn Parser + TypeScript Emitter kombináción átfuttatva az alábbi eredményt kapjuk:

raytracer-memory-2

Az Internet Explorer Developer Toolsban (F12) lévő UI Responsiveness eszköz segítségével megmértük a CPU utilization és a Visual throughput (FPS) értékeket is, és mindkét mutató jelentősen jobb értékeket mutatott.

Persze a kedvezőbb memória gazdálkodás és a jobb teljesítmény nem jön ingyen, a generált kód nagyobb lett, összesen 1844 sor. A jelentős méretnövekedést az okozza, hogy az IL kóddal ellentétben a szintakszisfa tartalmaz információt az osztályokról és azokon belül a tagok láthatóságáról, amit JavaScriptben csak körülményesen lehet leképezni, de ha megtesszük (és a TypeScript Emitter meg tudja tenni), akkor több kód letöltése árán összességében jobb teljesítményt tudunk elérni. Más alkalmazások vizsgálata során is ugyanerre a megállapításra jutottunk.

A módszer természetesen nem csak böngésző alapú alkalmazásoknál, hanem natív JavaScript alkalmazásoknál, például Node.js környezetben is működik. A fejlesztés lépései a következők:

  1. Telepítsd a Node.js Tools for Visual Studio bővítményt, ami lehetővé teszi Node.js appok készítését Visual Studioval.
  2. Töltsd le a Node.js with C# bővítményt a Visual Studio Gallery oldaláról (hamarosan elérhetővé tesszük).
  3. Telepítés után meg fog jelenni Visual Studioban a C# projektek között egy “Node.js application” nevű projekt sablon, annak segítségével kell létrehozni a projektet.
  4. Írd meg az alkalmazásod forráskódját C#-ban, ehhez természetesen használhatod a Visual Studiot.
  5. A szokásos módon működik a debuggolás (F5) és a futtatás is. A projekt sablon tartalmazza a szükséges MSBuild targeteket, ami a Roslyn és a TypeScript segítségével előállítja a JavaScript kódot, amit azután a Node.js Tools for Visual Studio fog átadni a Node.js futtatókörnyezetnek.

Tesztelőket keresünk! A Visual Studio plugin publikálása előtt szeretnénk egy szélesebb körű tesztet végezni. Ha szívesen vállalkoznál erre, kérlek itt olvasd el az útmutatót, majd hagyj egy kommentet alább és felveszem veled a kapcsolatot!

 

Frissítés:

reddit-node

http://www.reddit.com/r/node/comments/31r872/write_your_nodejs_app_in_c_with_roslyn/

 

5 thoughts on “C# kód futtatása Node.js környezetben Roslyn segítségével

  1. eMeL

    Pedig fordul🙂

    Volt egy project (MS alkalmazottak, de cégesen nem támogatott) ahol C# forrásból JS kód fordult.

    Természetesen csak az erre a célra épített osztálykönyvtár elemeiből🙂
    [ami a JS környezetet és a DOM-ot reprezentálta]

  2. madve2

    Igen, Script#-nak hívták (sőt, még mindig megvan: https://github.com/nikhilk/scriptsharp), HTML5 játékok fejlesztéséhez nagyon jó volt használni, intellisense a Canvas API-ra, visitor pattern a játékobjektumokhoz meg minden. A lelkem mélyén szeretem azt gondolni, hogy ez a projekt adta az inspirációt a TypeScripthez.

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s