.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1689 lines
43 KiB

//
// driver.cs: The compiler command line driver.
//
// Authors:
// Miguel de Icaza (miguel@gnu.org)
// Marek Safar (marek.safar@gmail.com)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
//
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
// Copyright 2004, 2005, 2006, 2007, 2008 Novell, Inc
//
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Globalization;
using System.Diagnostics;
namespace Mono.CSharp
{
/// <summary>
/// The compiler driver.
/// </summary>
class Driver
{
string first_source;
internal int fatal_errors;
internal readonly CompilerContext ctx;
static readonly char[] argument_value_separator = new char [] { ';', ',' };
private Driver (CompilerContext ctx)
{
this.ctx = ctx;
}
public static Driver Create (string[] args, bool require_files, Func<string [], int, int> unknown_option_parser, ReportPrinter printer)
{
Driver d = new Driver (new CompilerContext (new Report (printer)));
if (!d.ParseArguments (args, require_files, unknown_option_parser))
return null;
return d;
}
Report Report {
get { return ctx.Report; }
}
void tokenize_file (CompilationUnit file, CompilerContext ctx)
{
Stream input;
try {
input = File.OpenRead (file.Name);
} catch {
Report.Error (2001, "Source file `" + file.Name + "' could not be found");
return;
}
using (input){
SeekableStreamReader reader = new SeekableStreamReader (input, RootContext.Encoding);
Tokenizer lexer = new Tokenizer (reader, file, ctx);
int token, tokens = 0, errors = 0;
while ((token = lexer.token ()) != Token.EOF){
tokens++;
if (token == Token.ERROR)
errors++;
}
Console.WriteLine ("Tokenized: " + tokens + " found " + errors + " errors");
}
return;
}
void Parse (CompilationUnit file, ModuleContainer module)
{
Stream input;
try {
input = File.OpenRead (file.Name);
} catch {
Report.Error (2001, "Source file `{0}' could not be found", file.Name);
return;
}
// Check 'MZ' header
if (input.ReadByte () == 77 && input.ReadByte () == 90) {
Report.Error (2015, "Source file `{0}' is a binary file and not a text file", file.Name);
input.Close ();
return;
}
input.Position = 0;
SeekableStreamReader reader = new SeekableStreamReader (input, RootContext.Encoding);
Parse (reader, file, module);
reader.Dispose ();
input.Close ();
}
void Parse (SeekableStreamReader reader, CompilationUnit file, ModuleContainer module)
{
CSharpParser parser = new CSharpParser (reader, file, module);
parser.parse ();
}
static void OtherFlags ()
{
Console.WriteLine (
"Other flags in the compiler\n" +
" --fatal[=COUNT] Makes errors after COUNT fatal\n" +
" --lint Enhanced warnings\n" +
" --parse Only parses the source file\n" +
" --runtime:VERSION Sets mscorlib.dll metadata version: v1, v2, v4\n" +
" --stacktrace Shows stack trace at error location\n" +
" --timestamp Displays time stamps of various compiler events\n" +
" -v Verbose parsing (for debugging the parser)\n" +
" --mcs-debug X Sets MCS debugging level to X\n");
}
static void Usage ()
{
Console.WriteLine (
"Mono C# compiler, Copyright 2001 - 2011 Novell, Inc.\n" +
"mcs [options] source-files\n" +
" --about About the Mono C# compiler\n" +
" -addmodule:M1[,Mn] Adds the module to the generated assembly\n" +
" -checked[+|-] Sets default aritmetic overflow context\n" +
" -clscheck[+|-] Disables CLS Compliance verifications\n" +
" -codepage:ID Sets code page to the one in ID (number, utf8, reset)\n" +
" -define:S1[;S2] Defines one or more conditional symbols (short: -d)\n" +
" -debug[+|-], -g Generate debugging information\n" +
" -delaysign[+|-] Only insert the public key into the assembly (no signing)\n" +
" -doc:FILE Process documentation comments to XML file\n" +
" -fullpaths Any issued error or warning uses absolute file path\n" +
" -help Lists all compiler options (short: -?)\n" +
" -keycontainer:NAME The key pair container used to sign the output assembly\n" +
" -keyfile:FILE The key file used to strongname the ouput assembly\n" +
" -langversion:TEXT Specifies language version: ISO-1, ISO-2, 3, Default, or Future\n" +
" -lib:PATH1[,PATHn] Specifies the location of referenced assemblies\n" +
" -main:CLASS Specifies the class with the Main method (short: -m)\n" +
" -noconfig Disables implicitly referenced assemblies\n" +
" -nostdlib[+|-] Does not reference mscorlib.dll library\n" +
" -nowarn:W1[,Wn] Suppress one or more compiler warnings\n" +
" -optimize[+|-] Enables advanced compiler optimizations (short: -o)\n" +
" -out:FILE Specifies output assembly name\n" +
" -pkg:P1[,Pn] References packages P1..Pn\n" +
" -platform:ARCH Specifies the target platform of the output assembly\n" +
" ARCH can be one of: anycpu, x86, x64 or itanium\n" +
" -recurse:SPEC Recursively compiles files according to SPEC pattern\n" +
" -reference:A1[,An] Imports metadata from the specified assembly (short: -r)\n" +
" -reference:ALIAS=A Imports metadata using specified extern alias (short: -r)\n" +
" -sdk:VERSION Specifies SDK version of referenced assemlies\n" +
" VERSION can be one of: 2 (default), 4\n" +
" -target:KIND Specifies the format of the output assembly (short: -t)\n" +
" KIND can be one of: exe, winexe, library, module\n" +
" -unsafe[+|-] Allows to compile code which uses unsafe keyword\n" +
" -warnaserror[+|-] Treats all warnings as errors\n" +
" -warnaserror[+|-]:W1[,Wn] Treats one or more compiler warnings as errors\n" +
" -warn:0-4 Sets warning level, the default is 4 (short -w:)\n" +
" -helpinternal Shows internal and advanced compiler options\n" +
"\n" +
"Resources:\n" +
" -linkresource:FILE[,ID] Links FILE as a resource (short: -linkres)\n" +
" -resource:FILE[,ID] Embed FILE as a resource (short: -res)\n" +
" -win32res:FILE Specifies Win32 resource file (.res)\n" +
" -win32icon:FILE Use this icon for the output\n" +
" @file Read response file for more options\n\n" +
"Options can be of the form -option or /option");
}
void TargetUsage ()
{
Report.Error (2019, "Invalid target type for -target. Valid options are `exe', `winexe', `library' or `module'");
}
static void About ()
{
Console.WriteLine (
"The Mono C# compiler is Copyright 2001-2011, Novell, Inc.\n\n" +
"The compiler source code is released under the terms of the \n"+
"MIT X11 or GNU GPL licenses\n\n" +
"For more information on Mono, visit the project Web site\n" +
" http://www.mono-project.com\n\n" +
"The compiler was written by Miguel de Icaza, Ravi Pratap, Martin Baulig, Marek Safar, Raja R Harinath, Atushi Enomoto");
Environment.Exit (0);
}
public static int Main (string[] args)
{
Location.InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
var crp = new ConsoleReportPrinter ();
Driver d = Driver.Create (args, true, null, crp);
if (d == null)
return 1;
crp.Fatal = d.fatal_errors;
if (d.Compile () && d.Report.Errors == 0) {
if (d.Report.Warnings > 0) {
Console.WriteLine ("Compilation succeeded - {0} warning(s)", d.Report.Warnings);
}
Environment.Exit (0);
return 0;
}
Console.WriteLine("Compilation failed: {0} error(s), {1} warnings",
d.Report.Errors, d.Report.Warnings);
Environment.Exit (1);
return 1;
}
static string [] LoadArgs (string file)
{
StreamReader f;
var args = new List<string> ();
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;
}
}
return args.ToArray ();
}
//
// 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;
}
void AddSourceFile (string f)
{
if (first_source == null)
first_source = f;
Location.AddFile (Report, f);
}
bool ParseArguments (string[] args, bool require_files, Func<string [], int, int> unknown_option_parser)
{
List<string> response_file_list = null;
bool parsing_options = true;
for (int i = 0; i < args.Length; i++) {
string arg = args [i];
if (arg.Length == 0)
continue;
if (arg [0] == '@') {
string [] extra_args;
string response_file = arg.Substring (1);
if (response_file_list == null)
response_file_list = new List<string> ();
if (response_file_list.Contains (response_file)) {
Report.Error (
1515, "Response file `" + response_file +
"' specified multiple times");
return false;
}
response_file_list.Add (response_file);
extra_args = LoadArgs (response_file);
if (extra_args == null) {
Report.Error (2011, "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 [0] == '-') {
if (UnixParseOption (arg, ref args, ref i))
continue;
// Try a -CSCOPTION
string csc_opt = "/" + arg.Substring (1);
if (CSCParseOption (csc_opt, ref args))
continue;
if (unknown_option_parser != null){
var ret = unknown_option_parser (args, i);
if (ret != -1){
i = ret;
return true;
}
}
Error_WrongOption (arg);
return false;
}
if (arg [0] == '/') {
if (CSCParseOption (arg, ref args))
continue;
// Need to skip `/home/test.cs' however /test.cs is considered as error
if (arg.Length < 2 || arg.IndexOf ('/', 2) == -1) {
Error_WrongOption (arg);
return false;
}
}
}
ProcessSourceFiles (arg, false);
}
if (require_files == false)
return true;
//
// If we are an exe, require a source file for the entry point
//
if (RootContext.Target == Target.Exe || RootContext.Target == Target.WinExe || RootContext.Target == Target.Module) {
if (first_source == null) {
Report.Error (2008, "No files to compile were specified");
return false;
}
}
//
// If there is nothing to put in the assembly, and we are not a library
//
if (first_source == null && RootContext.Resources == null) {
Report.Error (2008, "No files to compile were specified");
return false;
}
return true;
}
public void Parse (ModuleContainer module)
{
Location.Initialize ();
var cu = Location.SourceFiles;
for (int i = 0; i < cu.Count; ++i) {
if (RootContext.TokenizeOnly) {
tokenize_file (cu [i], ctx);
} else {
Parse (cu [i], module);
}
}
}
void ProcessSourceFiles (string spec, bool recurse)
{
string path, pattern;
SplitPathAndPattern (spec, out path, out pattern);
if (pattern.IndexOf ('*') == -1){
AddSourceFile (spec);
return;
}
string [] files = null;
try {
files = Directory.GetFiles (path, pattern);
} catch (System.IO.DirectoryNotFoundException) {
Report.Error (2001, "Source file `" + spec + "' could not be found");
return;
} catch (System.IO.IOException){
Report.Error (2001, "Source file `" + spec + "' could not be found");
return;
}
foreach (string f in files) {
AddSourceFile (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
ProcessSourceFiles (d + "/" + pattern, true);
}
}
void SetWarningLevel (string s)
{
int level = -1;
try {
level = Int32.Parse (s);
} catch {
}
if (level < 0 || level > 4){
Report.Error (1900, "Warning level must be in the range 0-4");
return;
}
Report.WarningLevel = level;
}
static void Version ()
{
string version = System.Reflection.Assembly.GetExecutingAssembly ().GetName ().Version.ToString ();
Console.WriteLine ("Mono C# compiler version {0}", version);
Environment.Exit (0);
}
//
// Currently handles the Unix-like command line options, but will be
// deprecated in favor of the CSCParseOption, which will also handle the
// options that start with a dash in the future.
//
bool UnixParseOption (string arg, ref string [] args, ref int i)
{
switch (arg){
case "-v":
CSharpParser.yacc_verbose_flag++;
return true;
case "--version":
Version ();
return true;
case "--parse":
RootContext.ParseOnly = true;
return true;
case "--main": case "-m":
Report.Warning (-29, 1, "Compatibility: Use -main:CLASS instead of --main CLASS or -m CLASS");
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
RootContext.MainClass = args [++i];
return true;
case "--unsafe":
Report.Warning (-29, 1, "Compatibility: Use -unsafe instead of --unsafe");
RootContext.Unsafe = true;
return true;
case "/?": case "/h": case "/help":
case "--help":
Usage ();
Environment.Exit (0);
return true;
case "--define":
Report.Warning (-29, 1, "Compatibility: Use -d:SYMBOL instead of --define SYMBOL");
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
RootContext.AddConditional (args [++i]);
return true;
case "--tokenize":
RootContext.TokenizeOnly = true;
return true;
case "-o":
case "--output":
Report.Warning (-29, 1, "Compatibility: Use -out:FILE instead of --output FILE or -o FILE");
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
RootContext.OutputFile = args [++i];
return true;
case "--checked":
Report.Warning (-29, 1, "Compatibility: Use -checked instead of --checked");
RootContext.Checked = true;
return true;
case "--stacktrace":
Report.Printer.Stacktrace = true;
return true;
case "--linkresource":
case "--linkres":
Report.Warning (-29, 1, "Compatibility: Use -linkres:VALUE instead of --linkres VALUE");
if ((i + 1) >= args.Length){
Usage ();
Report.Error (5, "Missing argument to --linkres");
Environment.Exit (1);
}
AddResource (new AssemblyResource (args[++i], args[i]));
return true;
case "--resource":
case "--res":
Report.Warning (-29, 1, "Compatibility: Use -res:VALUE instead of --res VALUE");
if ((i + 1) >= args.Length){
Usage ();
Report.Error (5, "Missing argument to --resource");
Environment.Exit (1);
}
AddResource (new AssemblyResource (args[++i], args[i], true));
return true;
case "--target":
Report.Warning (-29, 1, "Compatibility: Use -target:KIND instead of --target KIND");
if ((i + 1) >= args.Length){
Environment.Exit (1);
return true;
}
string type = args [++i];
switch (type){
case "library":
RootContext.Target = Target.Library;
RootContext.TargetExt = ".dll";
break;
case "exe":
RootContext.Target = Target.Exe;
break;
case "winexe":
RootContext.Target = Target.WinExe;
break;
case "module":
RootContext.Target = Target.Module;
RootContext.TargetExt = ".dll";
break;
default:
TargetUsage ();
break;
}
return true;
case "-r":
Report.Warning (-29, 1, "Compatibility: Use -r:LIBRARY instead of -r library");
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
string val = args [++i];
int idx = val.IndexOf ('=');
if (idx > -1) {
string alias = val.Substring (0, idx);
string assembly = val.Substring (idx + 1);
AddAssemblyReference (alias, assembly);
return true;
}
AddAssemblyReference (val);
return true;
case "-L":
Report.Warning (-29, 1, "Compatibility: Use -lib:ARG instead of --L arg");
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
RootContext.ReferencesLookupPaths.Add (args [++i]);
return true;
case "--lint":
RootContext.EnhancedWarnings = true;
return true;
case "--nostdlib":
Report.Warning (-29, 1, "Compatibility: Use -nostdlib instead of --nostdlib");
RootContext.StdLib = false;
return true;
case "--nowarn":
Report.Warning (-29, 1, "Compatibility: Use -nowarn instead of --nowarn");
if ((i + 1) >= args.Length){
Usage ();
Environment.Exit (1);
}
int warn = 0;
try {
warn = Int32.Parse (args [++i]);
} catch {
Usage ();
Environment.Exit (1);
}
Report.SetIgnoreWarning (warn);
return true;
case "--wlevel":
Report.Warning (-29, 1, "Compatibility: Use -warn:LEVEL instead of --wlevel LEVEL");
if ((i + 1) >= args.Length){
Report.Error (
1900,
"--wlevel requires a value from 0 to 4");
Environment.Exit (1);
}
SetWarningLevel (args [++i]);
return true;
case "--mcs-debug":
if ((i + 1) >= args.Length){
Report.Error (5, "--mcs-debug requires an argument");
Environment.Exit (1);
}
try {
Report.DebugFlags = Int32.Parse (args [++i]);
} catch {
Report.Error (5, "Invalid argument to --mcs-debug");
Environment.Exit (1);
}
return true;
case "--about":
About ();
return true;
case "--recurse":
Report.Warning (-29, 1, "Compatibility: Use -recurse:PATTERN option instead --recurse PATTERN");
if ((i + 1) >= args.Length){
Report.Error (5, "--recurse requires an argument");
Environment.Exit (1);
}
ProcessSourceFiles (args [++i], true);
return true;
case "--timestamp":
RootContext.Timestamps = true;
return true;
case "--debug": case "-g":
Report.Warning (-29, 1, "Compatibility: Use -debug option instead of -g or --debug");
RootContext.GenerateDebugInfo = true;
return true;
case "--noconfig":
Report.Warning (-29, 1, "Compatibility: Use -noconfig option instead of --noconfig");
RootContext.LoadDefaultReferences = false;
return true;
default:
if (arg.StartsWith ("--fatal")){
if (arg.StartsWith ("--fatal=")){
if (!Int32.TryParse (arg.Substring (8), out fatal_errors))
fatal_errors = 1;
} else
fatal_errors = 1;
return true;
}
if (arg.StartsWith ("--runtime:", StringComparison.Ordinal)) {
string version = arg.Substring (10);
switch (version) {
case "v1":
case "V1":
RootContext.StdLibRuntimeVersion = RuntimeVersion.v1;
break;
case "v2":
case "V2":
RootContext.StdLibRuntimeVersion = RuntimeVersion.v2;
return true;
case "v4":
case "V4":
RootContext.StdLibRuntimeVersion = RuntimeVersion.v4;
return true;
}
return true;
}
break;
}
return false;
}
public static string GetPackageFlags (string packages, bool fatal, Report report)
{
ProcessStartInfo pi = new ProcessStartInfo ();
pi.FileName = "pkg-config";
pi.RedirectStandardOutput = true;
pi.UseShellExecute = false;
pi.Arguments = "--libs " + packages;
Process p = null;
try {
p = Process.Start (pi);
} catch (Exception e) {
report.Error (-27, "Couldn't run pkg-config: " + e.Message);
if (fatal)
Environment.Exit (1);
p.Close ();
return null;
}
if (p.StandardOutput == null){
report.Warning (-27, 1, "Specified package did not return any information");
p.Close ();
return null;
}
string pkgout = p.StandardOutput.ReadToEnd ();
p.WaitForExit ();
if (p.ExitCode != 0) {
report.Error (-27, "Error running pkg-config. Check the above output.");
if (fatal)
Environment.Exit (1);
p.Close ();
return null;
}
p.Close ();
return pkgout;
}
//
// This parses the -arg and /arg options to the compiler, even if the strings
// in the following text use "/arg" on the strings.
//
bool CSCParseOption (string option, ref string [] args)
{
int idx = option.IndexOf (':');
string arg, value;
if (idx == -1){
arg = option;
value = "";
} else {
arg = option.Substring (0, idx);
value = option.Substring (idx + 1);
}
switch (arg.ToLowerInvariant ()){
case "/nologo":
return true;
case "/t":
case "/target":
switch (value){
case "exe":
RootContext.Target = Target.Exe;
break;
case "winexe":
RootContext.Target = Target.WinExe;
break;
case "library":
RootContext.Target = Target.Library;
RootContext.TargetExt = ".dll";
break;
case "module":
RootContext.Target = Target.Module;
RootContext.TargetExt = ".netmodule";
break;
default:
TargetUsage ();
break;
}
return true;
case "/out":
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
RootContext.OutputFile = value;
return true;
case "/o":
case "/o+":
case "/optimize":
case "/optimize+":
RootContext.Optimize = true;
return true;
case "/o-":
case "/optimize-":
RootContext.Optimize = false;
return true;
// TODO: Not supported by csc 3.5+
case "/incremental":
case "/incremental+":
case "/incremental-":
// nothing.
return true;
case "/d":
case "/define": {
if (value.Length == 0){
Usage ();
Environment.Exit (1);
}
foreach (string d in value.Split (argument_value_separator)) {
string conditional = d.Trim ();
if (!Tokenizer.IsValidIdentifier (conditional)) {
Report.Warning (2029, 1, "Invalid conditional define symbol `{0}'", conditional);
continue;
}
RootContext.AddConditional (conditional);
}
return true;
}
case "/bugreport":
//
// We should collect data, runtime, etc and store in the file specified
//
Console.WriteLine ("To file bug reports, please visit: http://www.mono-project.com/Bugs");
return true;
case "/pkg": {
string packages;
if (value.Length == 0){
Usage ();
Environment.Exit (1);
}
packages = String.Join (" ", value.Split (new Char [] { ';', ',', '\n', '\r'}));
string pkgout = GetPackageFlags (packages, true, Report);
if (pkgout != null){
string [] xargs = pkgout.Trim (new Char [] {' ', '\n', '\r', '\t'}).
Split (new Char [] { ' ', '\t'});
args = AddArgs (args, xargs);
}
return true;
}
case "/linkres":
case "/linkresource":
case "/res":
case "/resource":
AssemblyResource res = null;
string[] s = value.Split (argument_value_separator, StringSplitOptions.RemoveEmptyEntries);
switch (s.Length) {
case 1:
if (s[0].Length == 0)
goto default;
res = new AssemblyResource (s [0], Path.GetFileName (s[0]));
break;
case 2:
res = new AssemblyResource (s [0], s [1]);
break;
case 3:
if (s [2] != "public" && s [2] != "private") {
Report.Error (1906, "Invalid resource visibility option `{0}'. Use either `public' or `private' instead", s [2]);
return true;
}
res = new AssemblyResource (s[0], s[1], s[2] == "private");
break;
default:
Report.Error (-2005, "Wrong number of arguments for option `{0}'", option);
break;
}
if (res != null) {
res.IsEmbeded = arg [1] == 'r' || arg [1] == 'R';
AddResource (res);
}
return true;
case "/recurse":
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
ProcessSourceFiles (value, true);
return true;
case "/r":
case "/reference": {
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
string[] refs = value.Split (argument_value_separator);
foreach (string r in refs){
if (r.Length == 0)
continue;
string val = r;
int index = val.IndexOf ('=');
if (index > -1) {
string alias = r.Substring (0, index);
string assembly = r.Substring (index + 1);
AddAssemblyReference (alias, assembly);
if (refs.Length != 1) {
Report.Error (2034, "Cannot specify multiple aliases using single /reference option");
break;
}
} else {
AddAssemblyReference (val);
}
}
return true;
}
case "/addmodule": {
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
string[] refs = value.Split (argument_value_separator);
foreach (string r in refs){
RootContext.Modules.Add (r);
}
return true;
}
case "/win32res": {
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
if (RootContext.Win32IconFile != null)
Report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time");
RootContext.Win32ResourceFile = value;
return true;
}
case "/win32icon": {
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
if (RootContext.Win32ResourceFile != null)
Report.Error (1565, "Cannot specify the `win32res' and the `win32ico' compiler option at the same time");
RootContext.Win32IconFile = value;
return true;
}
case "/doc": {
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
RootContext.Documentation = new Documentation (value);
return true;
}
case "/lib": {
string [] libdirs;
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
libdirs = value.Split (argument_value_separator);
foreach (string dir in libdirs)
RootContext.ReferencesLookupPaths.Add (dir);
return true;
}
case "/debug-":
RootContext.GenerateDebugInfo = false;
return true;
case "/debug":
if (value == "full" || value == "")
RootContext.GenerateDebugInfo = true;
return true;
case "/debug+":
RootContext.GenerateDebugInfo = true;
return true;
case "/checked":
case "/checked+":
RootContext.Checked = true;
return true;
case "/checked-":
RootContext.Checked = false;
return true;
case "/clscheck":
case "/clscheck+":
RootContext.VerifyClsCompliance = true;
return true;
case "/clscheck-":
RootContext.VerifyClsCompliance = false;
return true;
case "/unsafe":
case "/unsafe+":
RootContext.Unsafe = true;
return true;
case "/unsafe-":
RootContext.Unsafe = false;
return true;
case "/warnaserror":
case "/warnaserror+":
if (value.Length == 0) {
Report.WarningsAreErrors = true;
} else {
foreach (string wid in value.Split (argument_value_separator))
Report.AddWarningAsError (wid);
}
return true;
case "/warnaserror-":
if (value.Length == 0) {
Report.WarningsAreErrors = false;
} else {
foreach (string wid in value.Split (argument_value_separator))
Report.RemoveWarningAsError (wid);
}
return true;
case "/warn":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
SetWarningLevel (value);
return true;
case "/nowarn": {
if (value.Length == 0){
Error_RequiresArgument (option);
break;
}
var warns = value.Split (argument_value_separator);
foreach (string wc in warns){
try {
if (wc.Trim ().Length == 0)
continue;
int warn = Int32.Parse (wc);
if (warn < 1) {
throw new ArgumentOutOfRangeException("warn");
}
Report.SetIgnoreWarning (warn);
} catch {
Report.Error (1904, "`{0}' is not a valid warning number", wc);
}
}
return true;
}
case "/noconfig":
RootContext.LoadDefaultReferences = false;
return true;
case "/platform":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
switch (value.ToLower (CultureInfo.InvariantCulture)) {
case "anycpu":
RootContext.Platform = Platform.AnyCPU;
break;
case "x86":
RootContext.Platform = Platform.X86;
break;
case "x64":
RootContext.Platform = Platform.X64;
break;
case "itanium":
RootContext.Platform = Platform.IA64;
break;
default:
Report.Error (1672, "Invalid platform type for -platform. Valid options are `anycpu', `x86', `x64' or `itanium'");
break;
}
return true;
case "/sdk":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
switch (value.ToLowerInvariant ()) {
case "2":
RootContext.SdkVersion = SdkVersion.v2;
break;
case "4":
RootContext.SdkVersion = SdkVersion.v4;
break;
default:
Report.Error (-26, "Invalid sdk version name");
break;
}
return true;
// We just ignore this.
case "/errorreport":
case "/filealign":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
return true;
case "/helpinternal":
OtherFlags ();
Environment.Exit(0);
return true;
case "/help":
case "/?":
Usage ();
Environment.Exit (0);
return true;
case "/main":
case "/m":
if (value.Length == 0){
Error_RequiresArgument (option);
break;
}
RootContext.MainClass = value;
return true;
case "/nostdlib":
case "/nostdlib+":
RootContext.StdLib = false;
return true;
case "/nostdlib-":
RootContext.StdLib = true;
return true;
case "/fullpaths":
RootContext.ShowFullPaths = true;
return true;
case "/keyfile":
if (value.Length == 0) {
Error_RequiresFileName (option);
break;
}
RootContext.StrongNameKeyFile = value;
return true;
case "/keycontainer":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
RootContext.StrongNameKeyContainer = value;
return true;
case "/delaysign+":
case "/delaysign":
RootContext.StrongNameDelaySign = true;
return true;
case "/delaysign-":
RootContext.StrongNameDelaySign = false;
return true;
case "/langversion":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
switch (value.ToLowerInvariant ()) {
case "iso-1":
RootContext.Version = LanguageVersion.ISO_1;
return true;
case "default":
RootContext.Version = LanguageVersion.Default;
RootContext.AddConditional ("__V2__");
return true;
case "iso-2":
RootContext.Version = LanguageVersion.ISO_2;
return true;
case "3":
RootContext.Version = LanguageVersion.V_3;
return true;
case "future":
RootContext.Version = LanguageVersion.Future;
return true;
}
Report.Error (1617, "Invalid -langversion option `{0}'. It must be `ISO-1', `ISO-2', `3' or `Default'", value);
return true;
case "/codepage":
if (value.Length == 0) {
Error_RequiresArgument (option);
break;
}
switch (value) {
case "utf8":
RootContext.Encoding = new UTF8Encoding();
break;
case "reset":
RootContext.Encoding = Encoding.Default;
break;
default:
try {
RootContext.Encoding = Encoding.GetEncoding (Int32.Parse (value));
} catch {
Report.Error (2016, "Code page `{0}' is invalid or not installed", value);
}
break;
}
return true;
default:
return false;
}
return true;
}
void Error_WrongOption (string option)
{
Report.Error (2007, "Unrecognized command-line option: `{0}'", option);
}
void Error_RequiresFileName (string option)
{
Report.Error (2005, "Missing file specification for `{0}' option", option);
}
void Error_RequiresArgument (string option)
{
Report.Error (2006, "Missing argument for `{0}' option", option);
}
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);
}
return new_args;
}
void AddAssemblyReference (string assembly)
{
RootContext.AssemblyReferences.Add (assembly);
}
void AddAssemblyReference (string alias, string assembly)
{
if (assembly.Length == 0) {
Report.Error (1680, "Invalid reference alias `{0}='. Missing filename", alias);
return;
}
if (!IsExternAliasValid (alias)) {
Report.Error (1679, "Invalid extern alias for -reference. Alias `{0}' is not a valid identifier", alias);
return;
}
RootContext.AssemblyReferencesAliases.Add (Tuple.Create (alias, assembly));
}
void AddResource (AssemblyResource res)
{
if (RootContext.Resources == null) {
RootContext.Resources = new List<AssemblyResource> ();
RootContext.Resources.Add (res);
return;
}
if (RootContext.Resources.Contains (res)) {
ctx.Report.Error (1508, "The resource identifier `{0}' has already been used in this assembly", res.Name);
return;
}
RootContext.Resources.Add (res);
}
static bool IsExternAliasValid (string identifier)
{
if (identifier.Length == 0)
return false;
if (identifier [0] != '_' && !Char.IsLetter (identifier [0]))
return false;
for (int i = 1; i < identifier.Length; i++) {
char c = identifier [i];
if (Char.IsLetter (c) || Char.IsDigit (c))
continue;
UnicodeCategory category = Char.GetUnicodeCategory (c);
if (category != UnicodeCategory.Format || category != UnicodeCategory.NonSpacingMark ||
category != UnicodeCategory.SpacingCombiningMark ||
category != UnicodeCategory.ConnectorPunctuation)
return false;
}
return true;
}
//
// Main compilation method
//
public bool Compile ()
{
TimeReporter tr = new TimeReporter (RootContext.Timestamps);
ctx.TimeReporter = tr;
tr.StartTotal ();
var module = new ModuleContainer (ctx);
RootContext.ToplevelTypes = module;
tr.Start (TimeReporter.TimerType.ParseTotal);
Parse (module);
tr.Stop (TimeReporter.TimerType.ParseTotal);
if (Report.Errors > 0)
return false;
if (RootContext.TokenizeOnly || RootContext.ParseOnly)
return true;
if (RootContext.ToplevelTypes.NamespaceEntry != null)
throw new InternalErrorException ("who set it?");
//
// Quick hack
//
var output_file = RootContext.OutputFile;
string output_file_name;
if (output_file == null) {
if (first_source == null) {
Report.Error (1562, "If no source files are specified you must specify the output file with -out:");
return false;
}
int pos = first_source.LastIndexOf ('.');
if (pos > 0)
output_file = first_source.Substring (0, pos) + RootContext.TargetExt;
else
output_file = first_source + RootContext.TargetExt;
output_file_name = output_file;
} else {
output_file_name = Path.GetFileName (output_file);
}
#if STATIC
var importer = new StaticImporter ();
var references_loader = new StaticLoader (importer, ctx);
tr.Start (TimeReporter.TimerType.AssemblyBuilderSetup);
var assembly = new AssemblyDefinitionStatic (module, references_loader, output_file_name, output_file);
assembly.Create (references_loader.Domain);
tr.Stop (TimeReporter.TimerType.AssemblyBuilderSetup);
// Create compiler types first even before any referenced
// assembly is loaded to allow forward referenced types from
// loaded assembly into compiled builder to be resolved
// correctly
tr.Start (TimeReporter.TimerType.CreateTypeTotal);
module.CreateType ();
importer.AddCompiledAssembly (assembly);
tr.Stop (TimeReporter.TimerType.CreateTypeTotal);
references_loader.LoadReferences (module);
tr.Start (TimeReporter.TimerType.PredefinedTypesInit);
if (!ctx.BuildinTypes.CheckDefinitions (module))
return false;
tr.Stop (TimeReporter.TimerType.PredefinedTypesInit);
references_loader.LoadModules (assembly, module.GlobalRootNamespace);
#else
var assembly = new AssemblyDefinitionDynamic (module, output_file_name, output_file);
module.SetDeclaringAssembly (assembly);
var importer = new ReflectionImporter (ctx.BuildinTypes);
assembly.Importer = importer;
var loader = new DynamicLoader (importer, ctx);
loader.LoadReferences (module);
if (!ctx.BuildinTypes.CheckDefinitions (module))
return false;
if (!assembly.Create (AppDomain.CurrentDomain, AssemblyBuilderAccess.Save))
return false;
module.CreateType ();
loader.LoadModules (assembly, module.GlobalRootNamespace);
#endif
tr.Start (TimeReporter.TimerType.ModuleDefinitionTotal);
module.Define ();
tr.Stop (TimeReporter.TimerType.ModuleDefinitionTotal);
if (Report.Errors > 0)
return false;
if (RootContext.Documentation != null &&
!RootContext.Documentation.OutputDocComment (
output_file, Report))
return false;
//
// Verify using aliases now
//
tr.Start (TimeReporter.TimerType.UsingVerification);
NamespaceEntry.VerifyAllUsing ();
tr.Stop (TimeReporter.TimerType.UsingVerification);
if (Report.Errors > 0){
return false;
}
assembly.Resolve ();
if (Report.Errors > 0)
return false;
tr.Start (TimeReporter.TimerType.EmitTotal);
assembly.Emit ();
tr.Stop (TimeReporter.TimerType.EmitTotal);
if (Report.Errors > 0){
return false;
}
tr.Start (TimeReporter.TimerType.CloseTypes);
module.CloseType ();
tr.Stop (TimeReporter.TimerType.CloseTypes);
tr.Start (TimeReporter.TimerType.Resouces);
assembly.EmbedResources ();
tr.Stop (TimeReporter.TimerType.Resouces);
if (Report.Errors > 0)
return false;
assembly.Save ();
#if STATIC
references_loader.Dispose ();
#endif
tr.StopTotal ();
tr.ShowStats ();
return (Report.Errors == 0);
}
}
public class CompilerCompilationUnit {
public ModuleContainer ModuleCompiled { get; set; }
public LocationsBag LocationsBag { get; set; }
public UsingsBag UsingsBag { get; set; }
public SpecialsBag SpecialsBag { get; set; }
}
//
// This is the only public entry point
//
public class CompilerCallableEntryPoint : MarshalByRefObject {
public static bool InvokeCompiler (string [] args, TextWriter error)
{
try {
StreamReportPrinter srp = new StreamReportPrinter (error);
Driver d = Driver.Create (args, true, delegate (string[] a, int i) { System.Console.WriteLine ("Unknown option:" + a[i]); return 0; }, srp);
if (d == null)
return false;
return d.Compile () && srp.ErrorsCount == 0;
} finally {
Reset ();
}
}
public static int[] AllWarningNumbers {
get {
return Report.AllWarnings;
}
}
public static void Reset ()
{
Reset (true);
}
public static void PartialReset ()
{
Reset (false);
}
public static void Reset (bool full_flag)
{
CSharpParser.yacc_verbose_flag = 0;
Location.Reset ();
if (!full_flag)
return;
RootContext.Reset (full_flag);
TypeManager.Reset ();
ArrayContainer.Reset ();
ReferenceContainer.Reset ();
PointerContainer.Reset ();
Parameter.Reset ();
Unary.Reset ();
UnaryMutator.Reset ();
Binary.Reset ();
ConstantFold.Reset ();
CastFromDecimal.Reset ();
StringConcat.Reset ();
NamespaceEntry.Reset ();
Attribute.Reset ();
AnonymousTypeClass.Reset ();
AnonymousMethodBody.Reset ();
AnonymousMethodStorey.Reset ();
SymbolWriter.Reset ();
Switch.Reset ();
Linq.QueryBlock.TransparentParameter.Reset ();
Convert.Reset ();
TypeInfo.Reset ();
}
public static CompilerCompilationUnit ParseFile (string[] args, Stream input, string inputFile, TextWriter reportStream)
{
return ParseFile (args, input, inputFile, new StreamReportPrinter (reportStream));
}
internal static object parseLock = new object ();
public static CompilerCompilationUnit ParseFile (string[] args, Stream input, string inputFile, ReportPrinter reportPrinter)
{
lock (parseLock) {
try {
Driver d = Driver.Create (args, false, null, reportPrinter);
if (d == null)
return null;
Location.AddFile (null, inputFile);
Location.Initialize ();
// TODO: encoding from driver
SeekableStreamReader reader = new SeekableStreamReader (input, Encoding.Default);
CompilerContext ctx = new CompilerContext (new Report (reportPrinter));
RootContext.ToplevelTypes = new ModuleContainer (ctx);
CompilationUnit unit = null;
try {
unit = (CompilationUnit) Location.SourceFiles [0];
} catch (Exception) {
string path = Path.GetFullPath (inputFile);
unit = new CompilationUnit (inputFile, path, 0);
}
CSharpParser parser = new CSharpParser (reader, unit, RootContext.ToplevelTypes);
parser.Lexer.TabSize = 1;
parser.Lexer.sbag = new SpecialsBag ();
parser.LocationsBag = new LocationsBag ();
parser.UsingsBag = new UsingsBag ();
parser.parse ();
return new CompilerCompilationUnit () { ModuleCompiled = RootContext.ToplevelTypes, LocationsBag = parser.LocationsBag, UsingsBag = parser.UsingsBag, SpecialsBag = parser.Lexer.sbag };
} finally {
Reset ();
}
}
}
}
}