// Cesar Lopez Nataren (cesar@ciencias.unam.mx)
//
// (C) 2003, Cesar Lopez Nataren
-// (C) 2005, Novell Inc. (http://www.novell.com)
+// (C) Copyright 2005, Novell Inc. (http://www.novell.com)
//
//
using System;
using System.IO;
+using System.Text;
using Microsoft.Vsa;
using Microsoft.JScript;
+using System.Reflection;
+using System.Collections;
using Microsoft.JScript.Vsa;
+using System.Reflection.Emit;
+using Mono.CSharp;
-class Driver {
- public static void Main (string [] args) {
+namespace Mono.JScript {
- if (args.Length < 1) {
- Console.WriteLine ("Usage: mjs filename.js");
+ class Driver {
+ //
+ // Assemblies references to be linked. Initialized with
+ // mscorlib.dll
+ //
+ private static ArrayList references;
+
+ // Lookup paths
+ private static ArrayList link_paths;
+
+ // jscript files
+ private static ArrayList files;
+
+ private static string first_source;
+
+ private static bool want_debugging_support = false;
+
+ private static string output_file;
+
+ private static Assembly [] assemblies = new Assembly [0];
+
+ private static bool StdLib;
+
+ private static void Usage ()
+ {
+ Console.WriteLine ("Mono JScript compiler\n" +
+ "(C) 2003 - 2004 Cesar Lopez Nataren\n" +
+ "(C) 2004 - 2005 Novell Inc (http://novell.com)\n\n" +
+ "mjs [options] source-file\n" +
+ " /about About the Mono JScript compiler\n" +
+ " /lib:PATH1,PATH2 Adds the paths to the assembly link path\n" +
+ " /r[eference]:ASS Reference the specified assembly\n");
+ }
+
+ private static void About ()
+ {
+ Console.WriteLine (
+ "The Mono JScript compiler is:\n" +
+ "(C) 2003 - 2004 Cesar Lopez Nataren\n" +
+ "(C) 2004 - 2005 Novell Inc.\n\n" +
+ "The compiler source code is released under the terms of both the MIT X11 and MPL\n" +
+ "The compiler was written by Cesar Lopez Nataren");
Environment.Exit (0);
}
- VsaEngine engine = new VsaEngine ();
- engine.InitVsaEngine ("mjs:com.mono-project", new MonoEngineSite ());
+ /// <summary>
+ /// Loads all assemblies referenced on the command line
+ /// </summary>
+ private static void LoadReferences ()
+ {
+ foreach (string r in references)
+ LoadAssembly (r, false);
+ return;
+ }
+
+ private static void LoadAssembly (string assembly, bool soft)
+ {
+ Assembly a;
+ string total_log = "";
+
+ try {
+ char [] path_chars = { '/', '\\' };
+
+ if (assembly.IndexOfAny (path_chars) != -1) {
+ a = Assembly.LoadFrom (assembly);
+ } else {
+ string ass = assembly;
+ if (ass.EndsWith (".dll") || ass.EndsWith (".exe"))
+ ass = assembly.Substring (0, assembly.Length - 4);
+ a = Assembly.Load (ass);
+ }
+ AddAssembly (a);
+
+ } catch (FileNotFoundException){
+ foreach (string dir in link_paths){
+ string full_path = Path.Combine (dir, assembly);
+ if (!assembly.EndsWith (".dll") && !assembly.EndsWith (".exe"))
+ full_path += ".dll";
+
+ try {
+ a = Assembly.LoadFrom (full_path);
+ AddAssembly (a);
+ return;
+ } catch (FileNotFoundException ff) {
+ total_log += ff.FusionLog;
+ continue;
+ }
+ }
+ if (!soft)
+ Console.WriteLine ("Cannot find assembly `" + assembly + "'" );
+ } catch (BadImageFormatException f) {
+ Console.WriteLine ("Cannot load assembly (bad file format)" + f.FusionLog);
+ } catch (FileLoadException f){
+ Console.WriteLine ("Cannot load assembly " + f.FusionLog);
+ } catch (ArgumentNullException){
+ Console.WriteLine ("Cannot load assembly (null argument)");
+ }
+ }
+
+ /// <summary>
+ /// Registers an assembly to load types from.
+ /// </summary>
+ private static void AddAssembly (Assembly a)
+ {
+ foreach (Assembly assembly in assemblies) {
+ if (a == assembly)
+ return;
+ }
+
+ int top = assemblies.Length;
+ Assembly [] n = new Assembly [top + 1];
+
+ assemblies.CopyTo (n, 0);
+
+ n [top] = a;
+ assemblies = n;
+ }
+
+ static string [] LoadArgs (string file)
+ {
+ StreamReader f;
+ ArrayList args = new ArrayList ();
+ string line;
+ try {
+ f = new StreamReader (file);
+ } catch {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder ();
+
+ while ((line = f.ReadLine ()) != null){
+ int t = line.Length;
+
+ for (int i = 0; i < t; i++){
+ char c = line [i];
+
+ if (c == '"' || c == '\''){
+ char end = c;
+
+ for (i++; i < t; i++){
+ c = line [i];
+
+ if (c == end)
+ break;
+ sb.Append (c);
+ }
+ } else if (c == ' '){
+ if (sb.Length > 0){
+ args.Add (sb.ToString ());
+ sb.Length = 0;
+ }
+ } else
+ sb.Append (c);
+ }
+ if (sb.Length > 0){
+ args.Add (sb.ToString ());
+ sb.Length = 0;
+ }
+ }
+
+ string [] ret_value = new string [args.Count];
+ args.CopyTo (ret_value, 0);
+
+ return ret_value;
+ }
+
+ //
+ // Returns the directory where the system assemblies are installed
+ //
+ static string GetSystemDir ()
+ {
+ return Path.GetDirectoryName (typeof (object).Assembly.Location);
- foreach (string fn in args) {
- IVsaCodeItem item = (IVsaCodeItem) engine.Items.CreateItem (fn, VsaItemType.Code, VsaItemFlag.None);
- item.SourceText = GetCodeFromFile (fn);
}
- engine.Compile ();
- }
- static string GetCodeFromFile (string fn)
- {
- try {
- StreamReader reader = new StreamReader (fn);
- return reader.ReadToEnd ();
- } catch (FileNotFoundException) {
- throw new JScriptException (JSError.FileNotFound);
- } catch (ArgumentNullException) {
- throw new JScriptException (JSError.FileNotFound);
- } catch (ArgumentException) {
- throw new JScriptException (JSError.FileNotFound);
- } catch (IOException) {
- throw new JScriptException (JSError.NoError);
- } catch (OutOfMemoryException) {
- throw new JScriptException (JSError.OutOfMemory);
+ static void SetOutputFile (string name)
+ {
+ output_file = name;
}
- }
-}
-class MonoEngineSite : IVsaSite {
- public void GetCompiledState (out byte [] pe, out byte [] debugInfo)
- {
- throw new NotImplementedException ();
- }
+ static void Version ()
+ {
+ string version = Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
+ Console.WriteLine ("Mono JScript compiler version {0}", version);
+ Environment.Exit (0);
+ }
+
+ static bool UnixParseOption (string arg, ref string [] args, ref int i)
+ {
+ switch (arg){
+ case "--version":
+ Version ();
+ return true;
+
+ case "/?": case "/h": case "/help":
+ case "--help":
+ Usage ();
+ Environment.Exit (0);
+ return true;
+ case "-o":
+ case "--output":
+ if ((i + 1) >= args.Length){
+ Usage ();
+ Environment.Exit (1);
+ }
+ SetOutputFile (args [++i]);
+ return true;
+ case "-r":
+ if ((i + 1) >= args.Length){
+ Usage ();
+ Environment.Exit (1);
+ }
+
+ references.Add (args [++i]);
+ return true;
+
+ case "-L":
+ if ((i + 1) >= args.Length){
+ Usage ();
+ Environment.Exit (1);
+ }
+ link_paths.Add (args [++i]);
+ return true;
+
+ case "--about":
+ About ();
+ return true;
+ }
+ return false;
+ }
- public object GetEventSourceInstance (string itemName, string eventSourceName)
- {
- throw new NotImplementedException ();
- }
+ //
+ // This parses the -arg and /arg options to the compiler, even if the strings
+ // in the following text use "/arg" on the strings.
+ //
+ static bool CSCParseOption (string option, ref string [] args, ref int i)
+ {
+ int idx = option.IndexOf (':');
+ string arg, value;
+ if (idx == -1){
+ arg = option;
+ value = "";
+ } else {
+ arg = option.Substring (0, idx);
- public object GetGlobalInstance (string name)
- {
- throw new NotImplementedException ();
- }
+ value = option.Substring (idx + 1);
+ }
+
+ switch (arg){
+ case "/nologo":
+ return true;
+
+ case "/out":
+ if (value == ""){
+ Usage ();
+ Environment.Exit (1);
+ }
+ SetOutputFile (value);
+ return true;
+
+ case "/r":
+ case "/reference": {
+ if (value == ""){
+ Console.WriteLine ("/reference requires an argument");
+ Environment.Exit (1);
+ }
+
+ string [] refs = value.Split (new char [] { ';', ',' });
+ foreach (string r in refs){
+ references.Add (r);
+ }
+ return true;
+ }
+
+ case "/lib": {
+ string [] libdirs;
+
+ if (value == ""){
+ Console.WriteLine ("/lib requires an argument");
+ Environment.Exit (1);
+ }
+
+ libdirs = value.Split (new Char [] { ',' });
+ foreach (string dir in libdirs)
+ link_paths.Add (dir);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static string [] AddArgs (string [] args, string [] extra_args)
+ {
+ string [] new_args;
+ new_args = new string [extra_args.Length + args.Length];
+
+ // if args contains '--' we have to take that into account
+ // split args into first half and second half based on '--'
+ // and add the extra_args before --
+ int split_position = Array.IndexOf (args, "--");
+ if (split_position != -1)
+ {
+ Array.Copy (args, new_args, split_position);
+ extra_args.CopyTo (new_args, split_position);
+ Array.Copy (args, split_position, new_args, split_position + extra_args.Length, args.Length - split_position);
+ }
+ else
+ {
+ args.CopyTo (new_args, 0);
+ extra_args.CopyTo (new_args, args.Length);
+ }
- public void Notify (string notify, object info)
- {
- throw new NotImplementedException ();
+ return new_args;
+ }
+
+ //
+ // Given a path specification, splits the path from the file/pattern
+ //
+ static void SplitPathAndPattern (string spec, out string path, out string pattern)
+ {
+ int p = spec.LastIndexOf ('/');
+ if (p != -1){
+ //
+ // Windows does not like /file.cs, switch that to:
+ // "\", "file.cs"
+ //
+ if (p == 0){
+ path = "\\";
+ pattern = spec.Substring (1);
+ } else {
+ path = spec.Substring (0, p);
+ pattern = spec.Substring (p + 1);
+ }
+ return;
+ }
+
+ p = spec.LastIndexOf ('\\');
+ if (p != -1){
+ path = spec.Substring (0, p);
+ pattern = spec.Substring (p + 1);
+ return;
+ }
+
+ path = ".";
+ pattern = spec;
+ }
+
+ static void ProcessFile (string f)
+ {
+ if (first_source == null)
+ first_source = f;
+
+ files.Add (f);
+ }
+
+ static void CompileFiles (string spec, bool recurse)
+ {
+ string path, pattern;
+
+ SplitPathAndPattern (spec, out path, out pattern);
+ if (pattern.IndexOf ('*') == -1){
+ ProcessFile (spec);
+ return;
+ }
+
+ string [] files = null;
+ try {
+ files = Directory.GetFiles (path, pattern);
+ } catch (System.IO.DirectoryNotFoundException) {
+ Console.WriteLine ("Source file `" + spec + "' could not be found");
+ return;
+ } catch (System.IO.IOException){
+ Console.WriteLine ("Source file `" + spec + "' could not be found");
+ return;
+ }
+ foreach (string f in files)
+ ProcessFile (f);
+
+ if (!recurse)
+ return;
+
+ string [] dirs = null;
+
+ try {
+ dirs = Directory.GetDirectories (path);
+ } catch {
+ }
+
+ foreach (string d in dirs) {
+
+ // Don't include path in this string, as each
+ // directory entry already does
+ CompileFiles (d + "/" + pattern, true);
+ }
+ }
+
+ internal static bool MainDriver (string [] args)
+ {
+ int i;
+ bool parsing_options = true;
+
+ references = new ArrayList ();
+ link_paths = new ArrayList ();
+ files = new ArrayList ();
+
+ Hashtable response_file_list = null;
+
+ for (i = 0; i < args.Length; i++){
+ string arg = args [i];
+ if (arg == "")
+ continue;
+
+ if (arg.StartsWith ("@")){
+ string [] extra_args;
+ string response_file = arg.Substring (1);
+
+ if (response_file_list == null)
+ response_file_list = new Hashtable ();
+
+ if (response_file_list.Contains (response_file)){
+ Console.WriteLine ("Response file `" + response_file + "' specified multiple times");
+ Environment.Exit (1);
+ }
+
+ response_file_list.Add (response_file, response_file);
+
+ extra_args = LoadArgs (response_file);
+ if (extra_args == null){
+ Console.WriteLine ("Unable to open response file: " + response_file);
+ return false;
+ }
+
+ args = AddArgs (args, extra_args);
+ continue;
+ }
+
+ if (parsing_options){
+ if (arg == "--"){
+ parsing_options = false;
+ continue;
+ }
+
+ if (arg.StartsWith ("-")){
+ if (UnixParseOption (arg, ref args, ref i))
+ continue;
+
+ // Try a -CSCOPTION
+ string csc_opt = "/" + arg.Substring (1);
+ if (CSCParseOption (csc_opt, ref args, ref i))
+ continue;
+ } else {
+ if (arg.StartsWith ("/")){
+ if (CSCParseOption (arg, ref args, ref i))
+ continue;
+ }
+ }
+ }
+ CompileFiles (arg, false);
+ }
+
+ //
+ // If there is nothing to put in the assembly, and we are not a library
+ //
+ if (first_source == null) /* && embedded_resources == null && resources == null) */ {
+ Console.WriteLine ("fatal error JS2026: No input sources specified");
+ return false;
+ }
+
+ //
+ // Load Core Library for default compilation
+ //
+ if (StdLib)
+ references.Insert (0, "mscorlib");
+
+
+ //
+ // Load assemblies required
+ //
+ link_paths.Add (GetSystemDir ());
+ link_paths.Add (Directory.GetCurrentDirectory ());
+ LoadReferences ();
+ return true;
+ }
+
+ //
+ // Entry point
+ //
+ private static void Main (string [] args) {
+
+ if (args.Length < 1) {
+ Usage ();
+ Environment.Exit (0);
+ }
+ MainDriver (args);
+ VsaEngine engine = new VsaEngine ();
+ engine.InitVsaEngine ("mjs:com.mono-project", new MonoEngineSite ());
+
+ foreach (string asm in references) {
+ IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm, VsaItemType.Reference, VsaItemFlag.None);
+ item.AssemblyName = asm;
+ }
+
+ string asm_name = String.Empty;
+
+ foreach (Assembly assembly in assemblies) {
+ asm_name = assembly.GetName ().FullName;
+ IVsaReferenceItem item = (IVsaReferenceItem) engine.Items.CreateItem (asm_name, VsaItemType.Reference, VsaItemFlag.None);
+ item.AssemblyName = asm_name;
+ }
+
+ foreach (string file in files) {
+ IVsaCodeItem item = (IVsaCodeItem) engine.Items.CreateItem (file, VsaItemType.Code, VsaItemFlag.None);
+ item.SourceText = GetCodeFromFile (file);
+ }
+ engine.SetOption ("debug", want_debugging_support);
+ engine.SetOption ("link_path", link_paths);
+ engine.SetOption ("first_source", first_source);
+ engine.SetOption ("assemblies", assemblies);
+ engine.Compile ();
+ }
+
+ static string GetCodeFromFile (string fn)
+ {
+ try {
+ StreamReader reader = new StreamReader (fn);
+ return reader.ReadToEnd ();
+ } catch (FileNotFoundException) {
+ throw new JScriptException (JSError.FileNotFound);
+ } catch (ArgumentNullException) {
+ throw new JScriptException (JSError.FileNotFound);
+ } catch (ArgumentException) {
+ throw new JScriptException (JSError.FileNotFound);
+ } catch (IOException) {
+ throw new JScriptException (JSError.NoError);
+ } catch (OutOfMemoryException) {
+ throw new JScriptException (JSError.OutOfMemory);
+ }
+ }
}
- public bool OnCompilerError (IVsaError error)
- {
- throw new NotImplementedException ();
+ class MonoEngineSite : IVsaSite {
+ public void GetCompiledState (out byte [] pe, out byte [] debugInfo)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public object GetEventSourceInstance (string itemName, string eventSourceName)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public object GetGlobalInstance (string name)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public void Notify (string notify, object info)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool OnCompilerError (IVsaError error)
+ {
+ throw new NotImplementedException ();
+ }
}
}