summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
blob: be4c397d7d80e4f738e4196c3098cd89cb364117 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* NSC -- new Scala compiler
 * Copyright 2005-2006 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$

package scala.tools.nsc.symtab

import compat.Platform.currentTime
import java.io.{File, IOException}

import scala.collection.mutable.HashMap
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{ClassPath, NameTransformer, Position}
import classfile.{ClassfileParser, SymblfileParser}
import Flags._

/** This class ...
 *
 *  @author  Martin Odersky
 *  @version 1.0
 */
abstract class SymbolLoaders {
  val global: Global
  import global._

  /** A lazy type that completes itself by calling parameter doComplete.
   *  Any linked modules/classes or module classes are also initialized.
   *  @param doComplete    The type completion procedure to be run.
   *                       It takes symbol to compkete as parameter and returns
   *                       name of file loaded for completion as a result.
   *                       Can throw an IOException on error.
   */
  abstract class SymbolLoader extends LazyType {

    /** Load source or class file for `root', return */
    protected def doComplete(root: Symbol): unit

    /** The kind of file that's processed by this loader */
    protected def kindString: String

    private var ok = false
/*
    private def setSource(sym: Symbol, sourceFile0: AbstractFile): unit = sym match {
      case clazz: ClassSymbol => if (sourceFile0 != null) clazz.sourceFile = sourceFile0;
      case _ => if (sourceFile0 != null) if (false) System.err.println("YYY: " + sym + " " + sourceFile0);
    }
*/
    def sourceFile : AbstractFile = null
    protected def sourceString : String

    override def complete(root: Symbol): unit = {
      try {
        val start = currentTime
        val currentphase = phase
        doComplete(root)
        phase = currentphase
        def source = kindString + " " + sourceString
        informTime("loaded " + source, start)
        if (root.rawInfo == this && root.linkedSym.rawInfo == this)
          throw new TypeError(source + " does not define " + root)
        ok = true
      } catch {
        case ex: IOException =>
          ok = false
          if (settings.debug.value) ex.printStackTrace();
          val msg = ex.getMessage()
          error(
            if (msg == null) "i/o error while loading " + root.name
            else "error while loading " + root.name + ", " + msg);
      }
      initRoot(root)
      if (!root.isPackageClass) initRoot(root.linkedSym)
    }

    override def load(root: Symbol): unit = complete(root)

    private def initRoot(root: Symbol): unit = {
      if (root.rawInfo == this) {
        def markAbsent(sym: Symbol) =
          if (sym != NoSymbol) sym.setInfo(if (ok) NoType else ErrorType);
        markAbsent(root)
        markAbsent(root.moduleClass)
      } else if (root.isClass && !root.isModuleClass) root.rawInfo.load(root)
    }
  }

  /** Load contents of a package
   */
  class PackageLoader(directory: global.classPath0.Context) extends SymbolLoader {
    // System.err.println("PACKAGE LOADER: " + directory)

    protected def sourceString = directory.toString()

    protected def doComplete(root: Symbol): unit = {
      assert(root.isPackageClass, root)
      val scope = newScope
      root.setInfo(new PackageClassInfoType(scope, root))

      /** Is the given name a valid input file base name? */
      def isValid(name: String): boolean =
        name.length() > 0 && (settings.XbytecodeRead.value ||
          (!name.endsWith("$class") && name.indexOf("$anon") == -1));

      def enterPackage(str: String, completer: SymbolLoader): unit = {
        val pkg = root.newPackage(NoPos, newTermName(str))
        pkg.moduleClass.setInfo(completer)
        pkg.setInfo(pkg.moduleClass.tpe)
        root.info.decls.enter(pkg)
      }

      def enterClassAndModule(str: String, completer: SymbolLoader): unit = {
        val owner = if (root.isRoot) definitions.EmptyPackageClass else root
        val name = newTermName(str)
        val clazz = owner.newClass(NoPos, name.toTypeName)
        val module = owner.newModule(NoPos, name)
        clazz.setInfo(completer)
        module.setInfo(completer)
        module.moduleClass.setInfo(moduleClassLoader)
        owner.info.decls.enter(clazz)
        owner.info.decls.enter(module)
/*
        if (completer.sourceFile != null) {
          clazz.sourceFile = completer.sourceFile;
          module.moduleClass.sourceFile = completer.sourceFile
        }
*/
        assert(clazz.linkedModuleOfClass == module, module)
        assert(module.linkedClassOfModule == clazz, clazz)
      }

      val classes  = new HashMap[String, global.classPath0.Context]
      val packages = new HashMap[String, global.classPath0.Context]
      for (val dir <- directory.entries) if (dir.location != null) {
        for (val file <- dir.location) {
          if (file.isDirectory && directory.validPackage(file.name) && !packages.isDefinedAt(file.name))
            packages(file.name) = directory.find(file.name, true);
          else if (!file.isDirectory && file.name.endsWith(".class")) {
            val name = file.name.substring(0, file.name.length() - (".class").length());
            if (isValid(name) && !classes.isDefinedAt(name)) {
              val clazz = directory.find(name, false)
              if (clazz != null) classes(name) = clazz
            }
          }
        }
      }
      for (val dir <- directory.entries) if (dir.source != null) {
        for (val file <- dir.source.location) {
          if (file.isDirectory && directory.validPackage(file.name) && !packages.isDefinedAt(file.name))
            packages(file.name) = directory.find(file.name, true);
          else if (dir.source.compile && !file.isDirectory && file.name.endsWith(".scala")) {
            val name = file.name.substring(0, file.name.length() - (".scala").length());
            if (isValid(name) && !classes.isDefinedAt(name)) {
              val source = directory.find(name, false)
              if (source != null) classes(name) = source
            }
          }
        }
      }
      //if (!packages.isEmpty) System.err.println("COMPLETE: " + packages)
      //if (! classes.isEmpty) System.err.println("COMPLETE: " + classes)
      // do classes first

      for (val Pair(name, file) <- classes.elements) {
        val loader = if (!file.isSourceFile) {
          new ClassfileLoader(file.classFile, file.sourceFile, file.sourcePath);
        } else {
          assert(file.sourceFile != null)
          new SourcefileLoader(file.sourceFile)
        }
        enterClassAndModule(name, loader)
      }
      for (val Pair(name, file) <- packages.elements)
        enterPackage(name, new PackageLoader(file))
    }
    protected def kindString: String = "directory path"
  }

/*
  private object symblfileParser extends SymblfileParser {
    val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global;
  }
*/

  class ClassfileLoader(classFile: AbstractFile, override val sourceFile: AbstractFile, sourcePath0: AbstractFile) extends SymbolLoader {
    private object classfileParser extends ClassfileParser {
      val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global
      override def sourcePath = sourcePath0 /* could be null */
    }
    protected def doComplete(root: Symbol): unit = {
      classfileParser.parse(classFile, root)
      if (sourceFile != null) root match {
        case clazz : ClassSymbol => clazz.sourceFile = sourceFile
        case _ =>
      }
    }
    protected def kindString: String = "class file"
    protected def sourceString = classFile.toString()
  }
/*
  class SymblfileLoader(file: AbstractFile) extends SymbolLoader(file) {
    protected def doComplete(root: Symbol): unit = symblfileParser.parse(file, root);
    protected def kindString: String = "symbl file";
  }
*/
  class SourcefileLoader(override val sourceFile: AbstractFile) extends SymbolLoader {
    protected def doComplete(root: Symbol): unit =
      global.currentRun.compileLate(sourceFile)
    protected def kindString: String = "source file"
    protected def sourceString = sourceFile.toString()
  }

  object moduleClassLoader extends SymbolLoader {
    protected def doComplete(root: Symbol): unit = root.sourceModule.initialize
    protected def kindString: String = "";
    protected def sourceString = "";
  }
}