summaryrefslogtreecommitdiff
path: root/src/msil/ch/epfl/lamp/compiler/msil/emit/MultipleFilesILPrinterVisitor.scala
blob: bbbbf40508c65ee3fcec536c17ccc03f252fb7f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * System.Reflection.Emit-like API for writing .NET assemblies in MSIL
 */


package ch.epfl.lamp.compiler.msil.emit

import java.io.File
import java.io.FileWriter
import java.io.BufferedWriter
import java.io.PrintWriter
import java.io.IOException
import java.util.Iterator
import java.util.Arrays

import ch.epfl.lamp.compiler.msil._
import ch.epfl.lamp.compiler.msil.emit
import ch.epfl.lamp.compiler.msil.util.Table

/**
 * The MSIL printer Visitor. It prints a complete
 * assembly into separate files. Then these files can be compiled by ilasm.
 *
 * @author Nikolay Mihaylov
 * @author Daniel Lorch
 * @version 1.0
 */
final class MultipleFilesILPrinterVisitor(destPath: String, sourceFilesPath: String) extends ILPrinterVisitor {
   /**
     * Visit an AssemblyBuilder
     */
    @throws(classOf[IOException])
    def caseAssemblyBuilder(assemblyBuilder: AssemblyBuilder) {
	ILPrinterVisitor.currAssembly = assemblyBuilder

	// first get the entryPoint
	this.entryPoint = assemblyBuilder.EntryPoint

	// all external assemblies
	as = assemblyBuilder.getExternAssemblies()
	scala.util.Sorting.quickSort(as)(assemblyNameComparator)  // Arrays.sort(as, assemblyNameComparator)

	// print each module
        val m: Array[Module] = assemblyBuilder.GetModules()
        nomembers = true
        for(i <- 0 until m.length) {
	    print(m(i).asInstanceOf[ModuleBuilder])
	}

        nomembers = false
        for(i <- 0 until m.length) {
	    print(m(i).asInstanceOf[ModuleBuilder])
	}
	ILPrinterVisitor.currAssembly = null
    }

    /**
     * Visit a ModuleBuilder
     */
    @throws(classOf[IOException])
    def caseModuleBuilder(module: ModuleBuilder) {
    val assemblyBuilder = ILPrinterVisitor.currAssembly.asInstanceOf[AssemblyBuilder]

	// print module declaration
	currentModule = module

	// global methods typically contain the main method
	if (!module.globalsCreated)
	    module.CreateGlobalFunctions()

        val m: Array[MethodInfo] = module.GetMethods()

	// "Types" contain all the classes
        val t: Array[Type] = module.GetTypes()
        for(i <- 0 until t.length) {
        val tBuilder       = t(i).asInstanceOf[TypeBuilder]
        val sourceFilename = tBuilder.sourceFilename
        val sourceFilepath = new File(tBuilder.sourceFilepath).getCanonicalPath
        val sourcePath     = new File(sourceFilesPath).getCanonicalPath
		var append         = false

        if(!sourceFilepath.startsWith(sourcePath)) {
            throw new IOException("Source file " + sourceFilename + " must lie inside sourcepath " + sourcePath)
        }

        assert(sourceFilepath.endsWith(".scala"), "Source file doesn't end with .scala")
        val relativeFilename = sourceFilepath.substring(sourcePath.length, sourceFilepath.length() - 6) + ".msil"
        val fileName         = new File(destPath, relativeFilename)
        if(assemblyBuilder.generatedFiles.contains(fileName.getPath)) {
            append = true
        } else {
            fileName.getParentFile().mkdirs()
            assemblyBuilder.generatedFiles += (fileName.getPath)
        }

	    out = new PrintWriter(new BufferedWriter(new FileWriter(fileName, append)))
		// only write assembly boilerplate and class prototypes
		if (!append && nomembers) {
			printAssemblyBoilerplate()

			print(".module \'"); print(module.Name); println("\'")
		    printAttributes(module)
        }

	    print(t(i).asInstanceOf[TypeBuilder])
	    out.close()
	}

    // now write the global methods (typically contains the "main" method)
	if(!nomembers) {
       val globalMethods: File = new File(destPath, ILPrinterVisitor.currAssembly.GetName().Name + ".msil")
       val append = assemblyBuilder.generatedFiles.contains(globalMethods.getPath)

		out = new PrintWriter(new BufferedWriter(new FileWriter(globalMethods, append)))

        // make sure we're the first in the list (ilasm uses the first file name to guess the output file name)
        assemblyBuilder.generatedFiles.insert(0, globalMethods.getPath)

		// if this file hasn't been created by one of the classes, write boilerplate
		if(!append) {
			printAssemblyBoilerplate()

			print(".module \'"); print(module.Name); println("\'")
		    printAttributes(module)
		}

                for(i <- 0 until m.length) {
	   		print(m(i).asInstanceOf[MethodBuilder])
		}

		out.close()
	}

	currentModule = null
    }

}  // class MultipleFilesILPrinterVisitor