Monday, June 13, 2005

Interactive Mode for JavaScript.NET

Ever since I read Don's post the other day pointing out the often-overlooked support for a powerful command-line scripting languge in Windows (Javascript), I've been meaning to post this. It's a batch file that will let you run JScript.NET files interactively. Since Javascript is a compiled language, it does this by compiling it to a temporary directory first and then running the compiled EXE. While this might sound slow, as it turns out it's quite acceptable for quick, scripting-type tasks, and it gives me what I've always wanted: a convenient scripting language with full, natural-sytnax access both to COM and the .NET libraries.


Much as I'd like to claim credit for the batch file, I can't. It was written by John Elliott (for whom I don't have a URL) and posted to the WinTech Off-Topic List. The only modification I made was to add the /fast- switch to the compile, which turns off “fast mode”. Near as I can tell (not yet being that familiar with JScript.NET), it makes the code somewhat slower, but enables a lot of the more “scripty” features of the language, such as the ability to add methods dynamically to BCL types. Anyway, here's the script. I called mine rjs.cmd, and stuck it in my \bin directory, which is in my path. Then I can launch scripts by simply running “rjs foo.js“.





@echo OFF

rem ///////////////////////////////////////////////////////////////////////
rem // js-net-script.cmd
rem // ====================================================================
rem //
rem // Compiles and runs a JScript.NET source file.
rem //


goto :INIT


rem ///////////////////////////////////////////////////////////////////////
rem // HELP procedure
rem // ==============
rem //
rem // Display brief help message
rem //


:HELP


 if defined TRACE %TRACE% [proc %0 %*]
 
 echo Compiles and runs a JScript.NET source file as a console application.
 echo.
 echo JS-NET-SCRIPT script [args]
 echo.
 echo   script       Path to the JScript.NET file you wish to compile and run.
 echo   args         Any arguments you wish to pass to the JScript.NET app.
 echo.


goto :EOF



rem ///////////////////////////////////////////////////////////////////////
rem // MAIN procedure
rem //


:MAIN


 if defined TRACE %TRACE% [proc %0 %*]


 rem // make sure our working directory exists.
 rem // this is where our build outputs will be stored temporarily.
 if not exist "%APPDATA%\js-net-script" mkdir "%APPDATA%\js-net-script"


 rem // determine the various path information we'll need to compile
 rem // and run our script.
 set _ScriptPath=%APPDATA%\js-net-script\%~nx1
 set _ExePath=%_ScriptPath%.exe
 set _LogPath=%_ExePath%.log


 rem // copy the script to ensure it is on a local disk.
 copy "%~f1" "%_ScriptPath%" >nul 2>&1
 if errorlevel 1 set ERR=%errorlevel% && goto :ERROR


 rem // compile the script. we use a debug build so that we can
 rem // break into the debugger. we also log the build results to a
 rem // log file, so that we can show them to the user if there is
 rem // a problem.
 %SYSTEMROOT%\Microsoft.NET\Framework\v1.1.4322\jsc.exe /out:"%_ExePath%" /target:exe /debug+ /fast- /nologo "%_ScriptPath%" > "%_LogPath%" 2>&1
 if errorlevel 1 set ERR=%errorlevel% && goto :ERROR


 rem // run the application, passing in arguments.
 "%_ExePath%" %*


 rem // delete the local copy of the script, and all build outputs.
 del /s /q "%_ScriptPath%*" >nul 2>&1


goto :EOF



rem ///////////////////////////////////////////////////////////////////////
rem // INIT procedure
rem //


:INIT


rem @if not "%ECHO%"=="" echo %ECHO%
@if not "%OS%"=="Windows_NT" goto DOSEXIT


rem Set local scope and call MAIN procedure
 
setlocal & pushd & set RET=


 set SCRIPTNAME=%~n0
 set SCRIPTPATH=%~f0
 
 if "%DEBUG%"=="1" (set TRACE=echo) else (set TRACE=rem)
 
 if /i {%1}=={/help} (call :HELP %2) & (goto :HELPEXIT)
 if /i {%1}=={/?} (call :HELP %2) & (goto :HELPEXIT)


 :INIT_MAIN


 set _SAVED_PROMPT=%PROMPT%
 set PROMPT==$g


 call :MAIN %*


 set PROMPT=%_SAVED_PROMPT%


 :HELPEXIT


 popd & endlocal & set RET=%RET%


goto :EOF



rem ///////////////////////////////////////////////////////////////////////
rem // ERROR procedure
rem // ===============
rem //
rem // Abnormal exit. Expects the errorlevel has been stored in %ERR%.
rem //


:ERROR


 if defined TRACE %TRACE% [ERROR! %0 %*]
 
 echo.
 echo WARNING: Script failed with errorlevel: %ERR%
 echo.
 echo   Current directory is:
 echo     %CD%
 echo   Command-line was:
 echo     %CMDCMDLINE%
 echo.


 rem // show the build log. note: we'll show it in the console, but we
 rem // could also popup notepad.exe
 echo Compiler output:
 echo.
 type "%_LogPath%"
 echo.


 rem notepad "%_LogPath%"


 rem // give the user a chance to see the results.
 rem // note: this is not necessary if we open the log file in notepad.
 rem // note: while paused here, you can copy the build outputs from
 rem // the working directory in %APPDATA% to run ildasm, etc.
 pause


 rem // clean up all the build outputs.
 del /s /q "%_ScriptPath%*" >nul 2>&1


goto :EOF



rem ///////////////////////////////////////////////////////////////////////
rem // DOSEXIT
rem // ====================================================================
rem //
rem // These must be the final lines in the script.
rem //


:DOSEXIT


 echo This script requires Windows NT or later.


rem ///////////////////////////////////////////////////////////////////////



4 comments:

  1. I tried this using v2.0.50215 on 'foo.js', and got a crash in foo.js.exe:



    EventType : clr20r3 P1 : foo.js.exe P2 : 0.0.0.0 P3 : 42ae1967

    P4 : microsoft.jscript P5 : 8.0.0.0 P6 : 4257531d P7 : 928

    P8 : 2f P9 : microsoft.jscript.jscript

    ReplyDelete
  2. Well, I guess that's to be expected with beta software. :p



    Not quite sure what to tell you here. What happens if you compile it and run it manually?

    ReplyDelete
  3. One of my favorites parts of the discussion was John's discovery of the LickArgumentsIntoShape method in a stack trace....

    ReplyDelete
  4. John is quite often both intentionally and unintentially amusing. :)

    ReplyDelete