| 1 | use System.Reflection |
|---|
| 2 | use System.Diagnostics |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | class StopCompilation |
|---|
| 6 | inherits Exception |
|---|
| 7 | """ |
|---|
| 8 | Thrown by the compiler when it must stop compilation due to errors. This can happen at the end |
|---|
| 9 | of the phases: |
|---|
| 10 | * parsing |
|---|
| 11 | * bind interface |
|---|
| 12 | * bind implementation |
|---|
| 13 | * code gen |
|---|
| 14 | """ |
|---|
| 15 | |
|---|
| 16 | var _comp as Compiler |
|---|
| 17 | |
|---|
| 18 | cue init(comp as Compiler) |
|---|
| 19 | base.init |
|---|
| 20 | _comp = comp |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | class UnexpectedInvocationException |
|---|
| 24 | inherits SystemException |
|---|
| 25 | """ |
|---|
| 26 | Throw this when a class is not expecting a particular method to be invoked at runtime |
|---|
| 27 | (even though it inherits it or must implement it for an interface). |
|---|
| 28 | """ |
|---|
| 29 | |
|---|
| 30 | cue init(obj as Object) |
|---|
| 31 | base.init('Not expecting invocation.') |
|---|
| 32 | |
|---|
| 33 | class LoadReferenceException inherits Exception |
|---|
| 34 | """ |
|---|
| 35 | Thrown when Loading a reference fails for some reason. |
|---|
| 36 | FileNotFoundException, FileLoadException |
|---|
| 37 | """ |
|---|
| 38 | var fileName ='' |
|---|
| 39 | var tag='' |
|---|
| 40 | |
|---|
| 41 | cue init(tag as String, fileName as String, msg as String) |
|---|
| 42 | base.init(msg) |
|---|
| 43 | .tag = tag |
|---|
| 44 | .fileName = fileName |
|---|
| 45 | |
|---|
| 46 | def toString as String is override |
|---|
| 47 | return '[.tag] [.fileName] [.message]' |
|---|
| 48 | |
|---|
| 49 | enum PlatformEnum |
|---|
| 50 | Microsoft |
|---|
| 51 | Novell |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | class BackEnd is abstract |
|---|
| 55 | """ |
|---|
| 56 | Holder for items specific to the backEnd implementation |
|---|
| 57 | """ |
|---|
| 58 | |
|---|
| 59 | var _tagToTypeName = Dictionary<of String, String>() |
|---|
| 60 | """ Map from Typename tags used in compiler to backend specific qualified Type name""" |
|---|
| 61 | |
|---|
| 62 | cue init(compiler as Compiler) |
|---|
| 63 | base.init |
|---|
| 64 | _name = '' |
|---|
| 65 | _runTimeLibFileName = 'rtl' |
|---|
| 66 | _runTimeLibNativeSourceFileName = 'rtlSrc.ext' |
|---|
| 67 | __compiler = compiler |
|---|
| 68 | |
|---|
| 69 | get name from var as String |
|---|
| 70 | |
|---|
| 71 | get cobraRuntimeLibFileName from _runTimeLibFileName as String |
|---|
| 72 | """Name of the Cobra RunTime Library file for this backend.""" |
|---|
| 73 | |
|---|
| 74 | get runTimeLibNativeSourceFileName from _runTimeLibNativeSourceFileName as String |
|---|
| 75 | """Name of the backEnd source file containing any native code support for Cobra.Lang.""" |
|---|
| 76 | |
|---|
| 77 | get compiler from __compiler as Compiler |
|---|
| 78 | |
|---|
| 79 | get tagToTypeName from var |
|---|
| 80 | """ Map from TypeName tags used in compiler to backend specific qualified TypeName""" |
|---|
| 81 | |
|---|
| 82 | def resolveTypeTag(qualifiedNameOrTag as String) as String |
|---|
| 83 | if qualifiedNameOrTag.contains('.') |
|---|
| 84 | qualifiedName = qualifiedNameOrTag |
|---|
| 85 | else |
|---|
| 86 | qualifiedName = .tagToTypeName[qualifiedNameOrTag] # type tag to backend qualifiedName |
|---|
| 87 | return qualifiedName |
|---|
| 88 | |
|---|
| 89 | def makePhases(phases as IList<of Phase>) is abstract |
|---|
| 90 | """ |
|---|
| 91 | Given a list of core phases for compilation complete it with additions (or even removals and |
|---|
| 92 | rearrangements if necessary) for this given backend. |
|---|
| 93 | """ |
|---|
| 94 | |
|---|
| 95 | def getRecommendedBuiltInType(parentNameSpace as NameSpace?, name as String) as String? is abstract |
|---|
| 96 | """ |
|---|
| 97 | If parentNameSpace is the main system namespace and name is a backEnd builtin type |
|---|
| 98 | return the Cobra equivalent language type name (or nil). |
|---|
| 99 | """ |
|---|
| 100 | |
|---|
| 101 | def computeOutName as String is abstract |
|---|
| 102 | """ |
|---|
| 103 | Return the binary file output name for compilation of files for this backend. |
|---|
| 104 | """ |
|---|
| 105 | |
|---|
| 106 | def genNativeModule(filename as String, verbosity as int) as Module? is abstract |
|---|
| 107 | """ |
|---|
| 108 | Check if a filename is a Native module and if so generate and return the Native module type for it |
|---|
| 109 | otherwise return nil. |
|---|
| 110 | """ |
|---|
| 111 | |
|---|
| 112 | def setupRunProcess(baseExe as String, fullExe as String) as Process is abstract |
|---|
| 113 | """ |
|---|
| 114 | Create and initialise the process to run the compiled program post compilation |
|---|
| 115 | setup varies on the backend (.Clr/Jvm) and platform. |
|---|
| 116 | """ |
|---|
| 117 | |
|---|
| 118 | def setDefaultUseDirectives(ns as NameSpace) is abstract |
|---|
| 119 | """ |
|---|
| 120 | Set the default Use directives into the given Namespace (usually topNamespace) for this backend. |
|---|
| 121 | """ |
|---|
| 122 | |
|---|
| 123 | def fixLibExtension(libRef as String) as String is abstract |
|---|
| 124 | """ |
|---|
| 125 | Augment given lib reference string with backend extension if not already have one. |
|---|
| 126 | """ |
|---|
| 127 | |
|---|
| 128 | def loadLibReference(reference as String) as bool is abstract |
|---|
| 129 | """ |
|---|
| 130 | Load the given library reference file using the current backend paradigms. |
|---|
| 131 | Return true if reference found and loaded correctly, false otherwise |
|---|
| 132 | """ |
|---|
| 133 | |
|---|
| 134 | def readSystemTypes is abstract |
|---|
| 135 | """ Read and Load System Types for backend for Compiler to make available""" |
|---|
| 136 | |
|---|
| 137 | def fixMemberSigs is abstract |
|---|
| 138 | """ |
|---|
| 139 | Most backends dont natively support nilablility, this marks/fixes the common backend |
|---|
| 140 | class signatures that should be marked nilable. |
|---|
| 141 | """ |
|---|
| 142 | |
|---|
| 143 | def installNativeMethods(box as Box, nativeType as NativeType) is abstract |
|---|
| 144 | """ |
|---|
| 145 | Setup so that static methods (on Primitives and some others) get installed/treated as normal methods |
|---|
| 146 | on the instance. |
|---|
| 147 | """ |
|---|
| 148 | |
|---|
| 149 | def isRunnableFile( fullExeFileName as String) as bool is abstract |
|---|
| 150 | """Test if given filename is an executable ( vs a library or something else) """ |
|---|
| 151 | |
|---|
| 152 | # Native Types access |
|---|
| 153 | def cobraNameForNativeBoxName(name as String) as String is abstract |
|---|
| 154 | """ |
|---|
| 155 | Returns name from backend library entries converted to cobra naming form. |
|---|
| 156 | """ |
|---|
| 157 | |
|---|
| 158 | get objectTypeProxy as AbstractTypeProxy is abstract |
|---|
| 159 | """BE TypeProxy for root of back end Object hierarchy.""" |
|---|
| 160 | |
|---|
| 161 | get typeTypeProxy as AbstractTypeProxy is abstract |
|---|
| 162 | """BE TypeProxy for BE notion of a class describing a Type.""" |
|---|
| 163 | |
|---|
| 164 | def nativeTypeProxy(type as NativeType) as NativeTypeProxy is abstract |
|---|
| 165 | """ |
|---|
| 166 | Return a Proxy placeHolder for a BackEnd Native Type. |
|---|
| 167 | """ |
|---|
| 168 | |
|---|
| 169 | def nativeType(type as dynamic) as NativeType is abstract |
|---|
| 170 | """ |
|---|
| 171 | Return a Native Type wrapped so we can use it without explicitly knowing what it is anywhere |
|---|
| 172 | else but the providing back end. Used by the backends to generate Types from Libraries. |
|---|
| 173 | """ |
|---|
| 174 | |
|---|
| 175 | def nativeTypeByName(qualifiedName as String) as NativeType is abstract |
|---|
| 176 | """ |
|---|
| 177 | Return a Native Type corresponding to the fully qualified name. |
|---|
| 178 | Abstract Type Literal tags used in the compiler directly can also be obtained through this |
|---|
| 179 | otherwise the qualified names are expected to conform to the platform back end naming. |
|---|
| 180 | """ |
|---|
| 181 | |
|---|
| 182 | def prepSystemObjectClass(box as Box) is abstract |
|---|
| 183 | """Setup additional or convenience members on the System Object class. """ |
|---|
| 184 | |
|---|
| 185 | def scanGenericArgs(box as Box) is abstract |
|---|
| 186 | """Scan a loaded Dll (generic) type and translate from native any generic Args.""" |
|---|
| 187 | |
|---|
| 188 | def scanNativeType(box as Box) is abstract |
|---|
| 189 | """Scan a loaded Dll type and convert its native Type info to Cobras form """ |
|---|
| 190 | |
|---|
| 191 | def scanNativeType(edcl as EnumDecl) is abstract |
|---|
| 192 | """Scan a loaded Dll Enum type and convert its native info to Cobras form """ |
|---|
| 193 | |
|---|
| 194 | def setUnderlyingType(edcl as EnumDecl) is abstract |
|---|
| 195 | """Set underlying Storage Type of the value of an Enum.""" |
|---|
| 196 | |
|---|
| 197 | def determineExtnNativeType(extn as Extension, nativeType as NativeType) as NativeType |
|---|
| 198 | """ |
|---|
| 199 | The real extended type is the type of the first argument of any method. |
|---|
| 200 | Find and make that type from the param list. |
|---|
| 201 | """ |
|---|
| 202 | throw Exception('Need implementation of determineExtnNativeType for backend to determine type for an extension') |
|---|
| 203 | |
|---|
| 204 | def handleNameSpaceNameCollision(ns as NameSpace, token as IToken, name as String) as NameSpace |
|---|
| 205 | """ |
|---|
| 206 | What to do if a namespace name collides with an existing symbol. |
|---|
| 207 | Some backends disallow this situation so its an error. |
|---|
| 208 | Others keep namespace and symbol tables separate so its perfectly |
|---|
| 209 | allowable. |
|---|
| 210 | """ |
|---|
| 211 | throw Exception('In [ns.name] there is a already non-namespace declaration named "[name]".') |
|---|
| 212 | |
|---|
| 213 | class BasicBackEnd inherits BackEnd |
|---|
| 214 | """ Stub BackEnd for tests. """ |
|---|
| 215 | |
|---|
| 216 | cue init(compiler as Compiler) |
|---|
| 217 | base.init(compiler) |
|---|
| 218 | _name = 'c#-clr(basic)' |
|---|
| 219 | _runTimeLibFileName = 'cobraRTL.libext' |
|---|
| 220 | _runTimeLibNativeSourceFileName = 'cobraRTL.ext' |
|---|
| 221 | |
|---|
| 222 | _tagToTypeName = { |
|---|
| 223 | 'Object': 'System.Object', |
|---|
| 224 | 'Type' : 'System.Type', |
|---|
| 225 | } |
|---|
| 226 | |
|---|
| 227 | def makePhases(phases as IList<of Phase>) is override |
|---|
| 228 | pass |
|---|
| 229 | |
|---|
| 230 | def getRecommendedBuiltInType(parentNameSpace as NameSpace?, name as String) as String? is override |
|---|
| 231 | return nil |
|---|
| 232 | |
|---|
| 233 | def computeOutName as String is override |
|---|
| 234 | return 'BasicOut' |
|---|
| 235 | |
|---|
| 236 | def genNativeModule(filename as String, verbosity as int) as Module? is override |
|---|
| 237 | return nil |
|---|
| 238 | |
|---|
| 239 | def setupRunProcess(baseExe as String, fullExe as String) as Process is override |
|---|
| 240 | p = Process() |
|---|
| 241 | p.startInfo.fileName = 'echo' |
|---|
| 242 | p.startInfo.arguments = 'basic BackEnd process - [baseExe]' |
|---|
| 243 | return p |
|---|
| 244 | |
|---|
| 245 | def setDefaultUseDirectives(ns as NameSpace) is override |
|---|
| 246 | pass |
|---|
| 247 | |
|---|
| 248 | def fixLibExtension(libRef as String) as String is override |
|---|
| 249 | if not libRef.endsWith('.libext') |
|---|
| 250 | libRef += '.libext' |
|---|
| 251 | return libRef |
|---|
| 252 | |
|---|
| 253 | def loadLibReference(reference as String) as bool is override |
|---|
| 254 | return true |
|---|
| 255 | |
|---|
| 256 | def readSystemTypes is override |
|---|
| 257 | pass |
|---|
| 258 | |
|---|
| 259 | def fixMemberSigs is override |
|---|
| 260 | pass |
|---|
| 261 | |
|---|
| 262 | def installNativeMethods(box as Box, nativeType as NativeType) is override |
|---|
| 263 | pass |
|---|
| 264 | |
|---|
| 265 | def isRunnableFile(fullExeFileName as String) as bool is override |
|---|
| 266 | return true |
|---|
| 267 | |
|---|
| 268 | # Native Type access |
|---|
| 269 | def cobraNameForNativeBoxName(name as String) as String is override |
|---|
| 270 | return name + '_BBE' |
|---|
| 271 | |
|---|
| 272 | def prepSystemObjectClass(box as Box) is override |
|---|
| 273 | pass |
|---|
| 274 | |
|---|
| 275 | def scanGenericArgs(box as Box) is override |
|---|
| 276 | pass |
|---|
| 277 | |
|---|
| 278 | def scanNativeType(box as Box) is override |
|---|
| 279 | pass |
|---|
| 280 | |
|---|
| 281 | def scanNativeType(edcl as EnumDecl) is override |
|---|
| 282 | pass |
|---|
| 283 | |
|---|
| 284 | def setUnderlyingType(edcl as EnumDecl) is override |
|---|
| 285 | pass |
|---|
| 286 | |
|---|
| 287 | # Types |
|---|
| 288 | get objectTypeProxy as AbstractTypeProxy is override |
|---|
| 289 | return ClrTypeProxy(Object) # for testing |
|---|
| 290 | |
|---|
| 291 | get typeTypeProxy as AbstractTypeProxy is override |
|---|
| 292 | return ClrTypeProxy(Type )# for testing |
|---|
| 293 | |
|---|
| 294 | def nativeTypeProxy(type as NativeType) as NativeTypeProxy is override |
|---|
| 295 | return ClrTypeProxy(type) #TODO: fix this to something non BE specific Tmp |
|---|
| 296 | |
|---|
| 297 | def nativeType(type) as NativeType is override |
|---|
| 298 | return ClrNativeType(type) #TODO: fix this to something non BE specific Tmp |
|---|
| 299 | |
|---|
| 300 | def nativeTypeByName(qualifiedName as String) as NativeType is override |
|---|
| 301 | return ClrNativeType(System.Object) #TODO: fix this to something non BE specific Tmp |
|---|
| 302 | |
|---|
| 303 | class Compiler implements ITypeProvider, IWarningRecorder, IErrorRecorder, ICompilerForNodes is partial |
|---|
| 304 | """ |
|---|
| 305 | General notes: |
|---|
| 306 | |
|---|
| 307 | To cope with nesting, the compiler keeps a stack of the three types of things that can be nested. Items are pushed and popped off these stacks during each of the "bind interface" and "bind implementation" phases. The three stacks are: |
|---|
| 308 | * nameSpaceStack - namespaces |
|---|
| 309 | * boxStack - boxes are classes, structs and interfaces |
|---|
| 310 | * codeMemberStack - methods, properties and inits |
|---|
| 311 | """ |
|---|
| 312 | |
|---|
| 313 | var _backEnd as BackEnd? |
|---|
| 314 | |
|---|
| 315 | var _nextSerialNum as int is shared |
|---|
| 316 | var _serialNum as int |
|---|
| 317 | var _verbosity as int |
|---|
| 318 | var _options as OptionValues |
|---|
| 319 | var _commandLineArgParser as ArgParser? |
|---|
| 320 | var _willPrintSuccessMsg = true |
|---|
| 321 | var _willWriteTestInvocation = false |
|---|
| 322 | var _htmlWriter as HtmlWriter? |
|---|
| 323 | |
|---|
| 324 | var _globalNS as NameSpace |
|---|
| 325 | var _modules as List<of Module> |
|---|
| 326 | |
|---|
| 327 | var _curModule as Module? # set during bindInt and bindImp |
|---|
| 328 | var _nodeStack as Stack<of INode> |
|---|
| 329 | var _nameSpaceStack as Stack<of NameSpace> |
|---|
| 330 | var _boxStack as Stack<of Box> |
|---|
| 331 | var _boxMemberStack as Stack<of IBoxMember> |
|---|
| 332 | var _codeMemberStack as Stack<of AbstractMethod> |
|---|
| 333 | |
|---|
| 334 | var _errors as List<of SourceException> |
|---|
| 335 | var _warnings as List<of SourceException> |
|---|
| 336 | var _messages as List<of SourceException> # all errors and warnings |
|---|
| 337 | var _messagesPerSourceLine as IDictionary<of String, List<of SourceException>> # key is 'filename:lineNum' |
|---|
| 338 | var _noWarningLines as Cobra.Lang.ISet<of String> # key is 'filename:lineNum' |
|---|
| 339 | |
|---|
| 340 | var _intermediateFileNames as List<of String> |
|---|
| 341 | var _loadedReferences as List<of String> |
|---|
| 342 | |
|---|
| 343 | |
|---|
| 344 | # caches |
|---|
| 345 | var _primitiveToITypeCache as IDictionary<of dynamic, IType>? # Key is backend type for a Type ( e.g System.Type) |
|---|
| 346 | |
|---|
| 347 | cue init |
|---|
| 348 | .init(0, nil) |
|---|
| 349 | |
|---|
| 350 | cue init(verbosity as int) |
|---|
| 351 | .init(verbosity, nil) |
|---|
| 352 | |
|---|
| 353 | cue init(verbosity as int, initialModules as IList<of Module>?) |
|---|
| 354 | base.init |
|---|
| 355 | Node.reset |
|---|
| 356 | _nextSerialNum += 1 |
|---|
| 357 | _serialNum = _nextSerialNum |
|---|
| 358 | _verbosity = verbosity |
|---|
| 359 | _modules = List<of Module>() |
|---|
| 360 | _options = OptionValues() |
|---|
| 361 | _pathsToCompile = List<of String>() |
|---|
| 362 | _globalNS = NameSpace(Token.empty, '(global)') |
|---|
| 363 | _nodeStack = Stack<of INode>() |
|---|
| 364 | _nameSpaceStack = Stack<of NameSpace>() |
|---|
| 365 | _boxStack = Stack<of Box>() |
|---|
| 366 | _boxMemberStack = Stack<of IBoxMember>() |
|---|
| 367 | _codeMemberStack = Stack<of AbstractMethod>() |
|---|
| 368 | _messages = List<of SourceException>() |
|---|
| 369 | _messagesPerSourceLine = Dictionary<of String, List<of SourceException>>() |
|---|
| 370 | _noWarningLines = Set<of String>() |
|---|
| 371 | _errors = List<of SourceException>() |
|---|
| 372 | _warnings = List<of SourceException>() |
|---|
| 373 | _intermediateFileNames = List<of String>() |
|---|
| 374 | _loadedReferences = List<of String>() |
|---|
| 375 | if initialModules |
|---|
| 376 | if false # TODO: not working yet |
|---|
| 377 | for mod in initialModules |
|---|
| 378 | if mod inherits AssemblyModule |
|---|
| 379 | ns = mod.topNameSpace |
|---|
| 380 | assert not ns.superNameSpace |
|---|
| 381 | ns.unifyInto(_globalNS) |
|---|
| 382 | # TODO: handle CobraModule too |
|---|
| 383 | _modules.add(mod) |
|---|
| 384 | |
|---|
| 385 | def toString as String is override |
|---|
| 386 | return '[.getType.name]([_serialNum])' |
|---|
| 387 | |
|---|
| 388 | get backEnd from var |
|---|
| 389 | |
|---|
| 390 | pro defaultOutName from var as String? |
|---|
| 391 | |
|---|
| 392 | pro verbosity from var |
|---|
| 393 | |
|---|
| 394 | get includeTests as bool |
|---|
| 395 | return .options.boolValue('include-tests') |
|---|
| 396 | |
|---|
| 397 | get primitiveToITypeCache from var |
|---|
| 398 | """ |
|---|
| 399 | Returns a cache mapping BackEnd types to their corresponding ITypes. |
|---|
| 400 | Populated and used by the backend TypeProxy implementations ( {Clr,Jvm,...}TypeProxy) |
|---|
| 401 | """ |
|---|
| 402 | set primitiveCache as IDictionary<of dynamic, IType> |
|---|
| 403 | assert value.count <> 0 |
|---|
| 404 | _primitiveToITypeCache = value |
|---|
| 405 | |
|---|
| 406 | get globalNS from var |
|---|
| 407 | """ |
|---|
| 408 | Holds all topLevel decls including refs to Namespaces used and referenced. |
|---|
| 409 | """ |
|---|
| 410 | |
|---|
| 411 | pro mainMethodTypeName from var as String = '' |
|---|
| 412 | |
|---|
| 413 | get modules from var |
|---|
| 414 | |
|---|
| 415 | pro options from var |
|---|
| 416 | |
|---|
| 417 | get pathsToCompile from var as IList<of String> |
|---|
| 418 | """ |
|---|
| 419 | Phases are free to modify this list (usually by adding new paths to include in compilation). |
|---|
| 420 | """ |
|---|
| 421 | |
|---|
| 422 | pro commandLineArgParser from var |
|---|
| 423 | |
|---|
| 424 | pro willPrintSuccessMsg from var |
|---|
| 425 | |
|---|
| 426 | pro willWriteTestInvocation from var |
|---|
| 427 | |
|---|
| 428 | get curPhase from var as Phase? |
|---|
| 429 | |
|---|
| 430 | pro curModule from var |
|---|
| 431 | |
|---|
| 432 | get lastPhase from var as Phase? |
|---|
| 433 | |
|---|
| 434 | get nodeStack from var |
|---|
| 435 | |
|---|
| 436 | get nameSpaceStack from var |
|---|
| 437 | |
|---|
| 438 | get curNameSpace as NameSpace |
|---|
| 439 | require .nameSpaceStack.count |
|---|
| 440 | return _nameSpaceStack.peek |
|---|
| 441 | |
|---|
| 442 | get boxStack from var |
|---|
| 443 | |
|---|
| 444 | get curBox as Box |
|---|
| 445 | require .boxStack.count |
|---|
| 446 | return _boxStack.peek |
|---|
| 447 | |
|---|
| 448 | get curBoxMember as IBoxMember |
|---|
| 449 | require .boxMemberStack.count |
|---|
| 450 | return _boxMemberStack.peek |
|---|
| 451 | |
|---|
| 452 | get boxMemberStack from var |
|---|
| 453 | |
|---|
| 454 | get curCodeMember as AbstractMethod |
|---|
| 455 | require .codeMemberStack.count |
|---|
| 456 | return _codeMemberStack.peek |
|---|
| 457 | |
|---|
| 458 | get codeMemberStack from var |
|---|
| 459 | |
|---|
| 460 | get errors from var |
|---|
| 461 | |
|---|
| 462 | get warnings from var |
|---|
| 463 | |
|---|
| 464 | get messages from var |
|---|
| 465 | |
|---|
| 466 | get noWarningLines from var |
|---|
| 467 | |
|---|
| 468 | get loadedReferences from var |
|---|
| 469 | """ |
|---|
| 470 | Returns the list of library references that were loaded. |
|---|
| 471 | This could be more than what's specified on the command line due to UseDirective. |
|---|
| 472 | And it could be less due to failures to load. |
|---|
| 473 | """ |
|---|
| 474 | |
|---|
| 475 | pro htmlWriter from var |
|---|
| 476 | """ |
|---|
| 477 | Set this to support the output-html command line option. |
|---|
| 478 | """ |
|---|
| 479 | |
|---|
| 480 | get hasDetailedStackTraceOption as bool |
|---|
| 481 | return _options.boolValue('detailed-stack-trace') |
|---|
| 482 | |
|---|
| 483 | get willTrackLocals as bool |
|---|
| 484 | # the box stack can be empty because of assembly; has SomeAttr |
|---|
| 485 | if .boxStack.count |
|---|
| 486 | # the code member stack can be empty due to class variables (ex: var _x = 1) |
|---|
| 487 | return .hasDetailedStackTraceOption and (.codeMemberStack.count == 0 or .curCodeMember.parentBox.canHaveDetailedStackTrace) |
|---|
| 488 | else |
|---|
| 489 | return false |
|---|
| 490 | |
|---|
| 491 | pro refExprLevel from var as int |
|---|
| 492 | |
|---|
| 493 | get hasExceptionReportOption as bool |
|---|
| 494 | return .options.boolValue('exception-report') |
|---|
| 495 | |
|---|
| 496 | pro linesCompiled from var as int |
|---|
| 497 | pro nodesCompiled from var as int |
|---|
| 498 | pro tokensCompiled from var as int |
|---|
| 499 | |
|---|
| 500 | def recordError(error as SourceException) |
|---|
| 501 | """ |
|---|
| 502 | Node calls this to record errors. |
|---|
| 503 | """ |
|---|
| 504 | if error inherits NodeMultiException |
|---|
| 505 | for exc in error.exceptions |
|---|
| 506 | _addMessage(exc) |
|---|
| 507 | else |
|---|
| 508 | _addMessage(error) |
|---|
| 509 | |
|---|
| 510 | def addIntermediateFile(path as String) |
|---|
| 511 | _intermediateFileNames.add(path) |
|---|
| 512 | |
|---|
| 513 | |
|---|
| 514 | ## Phases |
|---|
| 515 | |
|---|
| 516 | def phaseClasses as List<of Type> |
|---|
| 517 | """ |
|---|
| 518 | Returns a list of phase classes in the order they should be run. |
|---|
| 519 | Note that the back-ends also add their own phases. |
|---|
| 520 | """ |
|---|
| 521 | return [ |
|---|
| 522 | BindRunTimeLibraryPhase, |
|---|
| 523 | ReadLibrariesPhase, |
|---|
| 524 | ParsePhase, |
|---|
| 525 | BindUsePhase, |
|---|
| 526 | BindInheritancePhase, |
|---|
| 527 | BindInterfacePhase, |
|---|
| 528 | ComputeMatchingBaseMembersPhase, |
|---|
| 529 | BindImplementationPhase, |
|---|
| 530 | IdentifyMainPhase, |
|---|
| 531 | SuggestDefaultNumberPhase, |
|---|
| 532 | ] |
|---|
| 533 | |
|---|
| 534 | def makePhases as List<of Phase> |
|---|
| 535 | """ |
|---|
| 536 | Returns a list of phase instances in the order they should be run. |
|---|
| 537 | Consults the back-end. |
|---|
| 538 | """ |
|---|
| 539 | phases = for phaseClass in .phaseClasses get phaseClass(this) to Phase |
|---|
| 540 | .backEnd.makePhases(phases) |
|---|
| 541 | if .options.boolValue('timeit') |
|---|
| 542 | phases.add(CountNodesPhase(this)) |
|---|
| 543 | for i, phase in phases.numbered, phase.stableOrder = i |
|---|
| 544 | phases.sort # see Phase.order and .compareTo |
|---|
| 545 | return phases |
|---|
| 546 | |
|---|
| 547 | def runPhase(phaseType as Type) as bool |
|---|
| 548 | require phaseType.isSubclassOf(Phase) |
|---|
| 549 | ensure result implies .errors.count > old .errors.count |
|---|
| 550 | return .runPhase(phaseType(this) to Phase) |
|---|
| 551 | |
|---|
| 552 | def runPhase(phase as Phase) as bool |
|---|
| 553 | require |
|---|
| 554 | phase.compiler is this |
|---|
| 555 | not phase.isRunning |
|---|
| 556 | ensure |
|---|
| 557 | phase.didRun |
|---|
| 558 | not phase.isRunning |
|---|
| 559 | .lastPhase is phase |
|---|
| 560 | result implies .errors.count > old .errors.count |
|---|
| 561 | body |
|---|
| 562 | oldErrorCount = .errors.count |
|---|
| 563 | _curPhase = phase |
|---|
| 564 | try |
|---|
| 565 | phase.run |
|---|
| 566 | .writeSourceCodeCorrections |
|---|
| 567 | return .errors.count > oldErrorCount |
|---|
| 568 | finally |
|---|
| 569 | _curPhase = nil |
|---|
| 570 | _lastPhase = phase |
|---|
| 571 | |
|---|
| 572 | |
|---|
| 573 | ## Compiler |
|---|
| 574 | |
|---|
| 575 | def compileFilesNamed(paths as IList<of String>) |
|---|
| 576 | .compileFilesNamed(paths, false, nil) |
|---|
| 577 | |
|---|
| 578 | def compileFilesNamed(paths as IList<of String>, writeTestInvocation as bool, stopCompilation as Predicate<of Compiler>?) |
|---|
| 579 | .initBackEnd |
|---|
| 580 | .defaultOutName = if(.options.buildStandardLibrary, 'Cobra.Lang.dll', paths[0]) |
|---|
| 581 | if .options.boolValue('compile-if-needed') |
|---|
| 582 | outName = .backEnd.computeOutName |
|---|
| 583 | if not .isCompilationNeeded(outName, paths) |
|---|
| 584 | return |
|---|
| 585 | |
|---|
| 586 | if .options.boolValue('reveal-internal-exceptions') |
|---|
| 587 | _compileFilesNamed(paths, writeTestInvocation, stopCompilation) |
|---|
| 588 | .printMessages |
|---|
| 589 | return |
|---|
| 590 | |
|---|
| 591 | try |
|---|
| 592 | _compileFilesNamed(paths, writeTestInvocation, stopCompilation) |
|---|
| 593 | catch StopCompilation |
|---|
| 594 | throw |
|---|
| 595 | catch exc as Exception |
|---|
| 596 | # unexpected exception -- only StopCompilation should be thrown |
|---|
| 597 | if exc inherits SourceException |
|---|
| 598 | if exc.hasSourceSite |
|---|
| 599 | .recordError(InternalError(exc.fileName, exc.lineNum, exc.message, exc)) |
|---|
| 600 | else |
|---|
| 601 | .recordError(InternalError(exc.message, exc)) |
|---|
| 602 | else if exc inherits AssertException |
|---|
| 603 | sn as SyntaxNode? |
|---|
| 604 | if exc.this inherits SyntaxNode |
|---|
| 605 | sn = exc.this to SyntaxNode |
|---|
| 606 | else if exc.info inherits SyntaxNode |
|---|
| 607 | sn = exc.info to SyntaxNode |
|---|
| 608 | if sn |
|---|
| 609 | .recordError(InternalError(sn.token.fileName, sn.token.lineNum, exc.message, exc)) |
|---|
| 610 | else |
|---|
| 611 | .recordError(InternalError(exc.message, exc)) |
|---|
| 612 | else |
|---|
| 613 | .recordError(InternalError(exc.message, exc)) |
|---|
| 614 | .printMessages |
|---|
| 615 | throw StopCompilation(this) |
|---|
| 616 | success |
|---|
| 617 | .printMessages |
|---|
| 618 | |
|---|
| 619 | def _compileFilesNamed(paths as IList<of String>, writeTestInvocation as bool, stopCompilation as Predicate<of Compiler>?) |
|---|
| 620 | timeit = .options.boolValue('timeit') and .verbosity |
|---|
| 621 | timings, totalElapsed = [], 0.0f |
|---|
| 622 | .willWriteTestInvocation = writeTestInvocation |
|---|
| 623 | _pathsToCompile = paths |
|---|
| 624 | # now, essentially, do this: |
|---|
| 625 | # for phase in .makePhases, runPhase(phase) |
|---|
| 626 | # but pay attention to things like errors and stopCompilation |
|---|
| 627 | hasErrors = false |
|---|
| 628 | for phase in .makePhases |
|---|
| 629 | if stopCompilation and stopCompilation(this), break |
|---|
| 630 | if not hasErrors or phase.willRunWithErrors |
|---|
| 631 | sw = System.Diagnostics.Stopwatch() |
|---|
| 632 | sw.start |
|---|
| 633 | if .runPhase(phase), hasErrors = true |
|---|
| 634 | sw.stop |
|---|
| 635 | if timeit |
|---|
| 636 | totalElapsed += sw.elapsed.totalSeconds |
|---|
| 637 | timings.add([sw.elapsed, phase.description]) |
|---|
| 638 | sw.reset |
|---|
| 639 | if timeit |
|---|
| 640 | # to see timings try: |
|---|
| 641 | # cobra -c -v -timeit hello.cobra |
|---|
| 642 | # timings.sort(do(a, b)=b[0].compareTo(a[0])) |
|---|
| 643 | sharp'timings.Sort(delegate(object a, object b) { return ((System.TimeSpan)((System.Collections.Generic.List<object>)b)[0]).CompareTo((System.TimeSpan)((System.Collections.Generic.List<object>)a)[0]); })' |
|---|
| 644 | print |
|---|
| 645 | print 'Phase timings:' |
|---|
| 646 | for timing in timings |
|---|
| 647 | elapsed, description = timing[0].totalSeconds to float, timing[1] to String |
|---|
| 648 | percent = 100 * elapsed / totalElapsed |
|---|
| 649 | print ' [percent:00.00]% [elapsed:00.00]secs [description]' |
|---|
| 650 | print ' 100.00% [totalElapsed:00.00]secs Total for all phases' |
|---|
| 651 | print |
|---|
| 652 | |
|---|
| 653 | if hasErrors, _exitFromErrors |
|---|
| 654 | |
|---|
| 655 | def testifyFilesNamed(fileNames as IList<of String>, options as OptionValues, resultsWriter as IndentedWriter, verbose as bool) |
|---|
| 656 | """ |
|---|
| 657 | Compiles the given fileNames in support of "testify". |
|---|
| 658 | Sets .options to the options arg. |
|---|
| 659 | Will raise StopCompilation when an error occurs. |
|---|
| 660 | """ |
|---|
| 661 | saveError = Console.error |
|---|
| 662 | Console.setError(CobraCore.printDestination) |
|---|
| 663 | try |
|---|
| 664 | .options = options |
|---|
| 665 | bar = '----------------------------------------------------------------------------------------------------' |
|---|
| 666 | .initBackEnd |
|---|
| 667 | .defaultOutName = if(.options.buildStandardLibrary, 'Cobra.Lang.dll', fileNames[0]) |
|---|
| 668 | _pathsToCompile = fileNames |
|---|
| 669 | hasErrors = false |
|---|
| 670 | for phase in .makePhases |
|---|
| 671 | if verbose, print 'Testify phase:', phase.description |
|---|
| 672 | if not hasErrors or phase.willRunWithErrors |
|---|
| 673 | if .runPhase(phase), hasErrors = true |
|---|
| 674 | if verbose |
|---|
| 675 | .dumpModulesForTestify(resultsWriter, 'Modules after: [phase.description]') |
|---|
| 676 | print bar |
|---|
| 677 | .printMessages |
|---|
| 678 | print bar |
|---|
| 679 | if hasErrors, throw StopCompilation(this) # testify runner wants this |
|---|
| 680 | # TODO: to display the intermediate source: |
|---|
| 681 | # for module in .modules |
|---|
| 682 | # if not module.isCobraLibrary |
|---|
| 683 | # Utils.printSource(module.sharpSource) |
|---|
| 684 | # print bar |
|---|
| 685 | finally |
|---|
| 686 | Console.setError(saveError) |
|---|
| 687 | |
|---|
| 688 | def isCompilationNeeded(outName as String, paths as IList<of String>) as bool |
|---|
| 689 | """ |
|---|
| 690 | Returns true if compilation is needed because the output file does not exist |
|---|
| 691 | or a source file is newer than it. |
|---|
| 692 | """ |
|---|
| 693 | v = .verbosity |
|---|
| 694 | if v, print 'Checking timestamps for -compile-if-needed' |
|---|
| 695 | if not File.exists(outName) |
|---|
| 696 | if v, print ' "[outName]" does not exist.' |
|---|
| 697 | return true |
|---|
| 698 | targetTime = File.getLastWriteTime(outName) |
|---|
| 699 | if v >= 2, print ' "[outName]" last write time: [targetTime].' |
|---|
| 700 | for path in paths |
|---|
| 701 | srcTime = File.getLastWriteTime(path) |
|---|
| 702 | if v >= 2, print ' "[path]" last write time: [srcTime].' |
|---|
| 703 | if srcTime.compareTo(targetTime) >= 0 |
|---|
| 704 | if v, print ' "[path]" is newer than "[outName]"; will compile.' |
|---|
| 705 | return true |
|---|
| 706 | if v, print ' No source file is newer than "[outName]"; skipping compile.' |
|---|
| 707 | return false |
|---|
| 708 | |
|---|
| 709 | def addRunTimeRef(opts as OptionValues) |
|---|
| 710 | if not opts.containsKey('reference') |
|---|
| 711 | opts['reference'] = List<of String>() |
|---|
| 712 | refs = opts['reference'] to List<of String> |
|---|
| 713 | libName = .backEnd.cobraRuntimeLibFileName |
|---|
| 714 | if libName not in refs |
|---|
| 715 | if .verbosity, print 'Adding reference to [libName]' |
|---|
| 716 | refs.add(libName) |
|---|
| 717 | |
|---|
| 718 | def runProcess as Process |
|---|
| 719 | return .runProcess(nil, nil) |
|---|
| 720 | |
|---|
| 721 | def runProcess(exeName as String?, argList as List<of String>?) as Process |
|---|
| 722 | """ |
|---|
| 723 | Returns a new Process with startInfo.fileName and p.startInfo.arguments set appropriately |
|---|
| 724 | for the produced executable and the current options and any provided argsList. |
|---|
| 725 | """ |
|---|
| 726 | baseExeFileName = exeName ? .baseExeFileName |
|---|
| 727 | fullExeFileName = exeName ? .fullExeFileName |
|---|
| 728 | p = .backEnd.setupRunProcess(baseExeFileName, fullExeFileName) |
|---|
| 729 | |
|---|
| 730 | if argList and argList.count |
|---|
| 731 | args = p.startInfo.arguments ? '' |
|---|
| 732 | if args <> '', args += ' ' |
|---|
| 733 | hasSpaces = any for a in argList where ' ' in a |
|---|
| 734 | if hasSpaces |
|---|
| 735 | argList = for arg in argList get if(' ' in arg, '"[arg]"', arg) |
|---|
| 736 | args += argList.join(' ') |
|---|
| 737 | p.startInfo.arguments = args |
|---|
| 738 | return p |
|---|
| 739 | |
|---|
| 740 | ## |
|---|
| 741 | ## ITypeProvider |
|---|
| 742 | ## |
|---|
| 743 | |
|---|
| 744 | var _anyFloatType as AnyFloatType? |
|---|
| 745 | var _anyIntType as AnyIntType? |
|---|
| 746 | var _arrayTypes as Dictionary<of IType, ArrayType>? |
|---|
| 747 | var _boolType as BoolType? |
|---|
| 748 | var _charType as CharType? |
|---|
| 749 | var _decimalType as DecimalType? |
|---|
| 750 | var _dynamicType as DynamicType? |
|---|
| 751 | var _floatTypes = Dictionary<of int, FloatType>() |
|---|
| 752 | var _intTypes = Dictionary<of int, IntType>() |
|---|
| 753 | var _nilType as NilType? |
|---|
| 754 | var _numberType as AbstractNumberType? |
|---|
| 755 | var _passThroughType as PassThroughType? |
|---|
| 756 | var _unspecifiedType as UnspecifiedType? |
|---|
| 757 | var _voidType as VoidType? |
|---|
| 758 | var _nilableTypes as Dictionary<of INode, NilableType>? |
|---|
| 759 | var _variTypes as Dictionary<of INode, VariType>? |
|---|
| 760 | var _nilableDynamicType as NilableType? |
|---|
| 761 | |
|---|
| 762 | def typeForName(name as String) as IType |
|---|
| 763 | assert false, 'TODO' |
|---|
| 764 | return .intType |
|---|
| 765 | |
|---|
| 766 | def typeOrNilForName(name as String) as IType? |
|---|
| 767 | assert false, 'TODO' |
|---|
| 768 | return nil |
|---|
| 769 | |
|---|
| 770 | get anyFloatType as AnyFloatType |
|---|
| 771 | if _anyFloatType is nil |
|---|
| 772 | _anyFloatType = AnyFloatType() |
|---|
| 773 | return _anyFloatType to ! |
|---|
| 774 | |
|---|
| 775 | get anyIntType as AnyIntType |
|---|
| 776 | if _anyIntType is nil |
|---|
| 777 | _anyIntType = AnyIntType() |
|---|
| 778 | return _anyIntType to ! |
|---|
| 779 | |
|---|
| 780 | def arrayType(type as IType) as ArrayType |
|---|
| 781 | if _arrayTypes is nil, _arrayTypes = Dictionary<of IType, ArrayType>() |
|---|
| 782 | if _arrayTypes.containsKey(type) |
|---|
| 783 | return _arrayTypes[type] |
|---|
| 784 | else |
|---|
| 785 | _arrayTypes[type] = at = ArrayType(type) |
|---|
| 786 | at.bindInh |
|---|
| 787 | at.bindInt |
|---|
| 788 | return at |
|---|
| 789 | |
|---|
| 790 | get boolType as BoolType |
|---|
| 791 | if _boolType is nil |
|---|
| 792 | _boolType = BoolType() |
|---|
| 793 | return _boolType to ! |
|---|
| 794 | |
|---|
| 795 | get charType as CharType |
|---|
| 796 | if _charType is nil |
|---|
| 797 | _charType = CharType() |
|---|
| 798 | return _charType to ! |
|---|
| 799 | |
|---|
| 800 | get decimalType as DecimalType |
|---|
| 801 | if _decimalType is nil |
|---|
| 802 | _decimalType = DecimalType() |
|---|
| 803 | return _decimalType to ! |
|---|
| 804 | |
|---|
| 805 | get dynamicType as DynamicType |
|---|
| 806 | if _dynamicType is nil |
|---|
| 807 | _dynamicType = DynamicType() |
|---|
| 808 | return _dynamicType to ! |
|---|
| 809 | |
|---|
| 810 | def floatType as FloatType |
|---|
| 811 | return .floatType(64) |
|---|
| 812 | |
|---|
| 813 | def floatType(size as int) as FloatType |
|---|
| 814 | require size in [32, 64] |
|---|
| 815 | type as FloatType? |
|---|
| 816 | if _floatTypes.tryGetValue(size, out type) |
|---|
| 817 | return type to ! |
|---|
| 818 | else |
|---|
| 819 | type = _floatTypes[size] = FloatType(size, .anyFloatType) |
|---|
| 820 | return type to ! |
|---|
| 821 | |
|---|
| 822 | def intType as IntType |
|---|
| 823 | return .intType(true, 32) |
|---|
| 824 | |
|---|
| 825 | def uintType as IntType |
|---|
| 826 | return .intType(false, 32) |
|---|
| 827 | |
|---|
| 828 | def intType(signed as bool, size as int) as IntType |
|---|
| 829 | require size in [8, 16, 32, 64] # CC: inherit from interface |
|---|
| 830 | key = if(signed, -1, +1) * size |
|---|
| 831 | type as IntType? |
|---|
| 832 | if _intTypes.tryGetValue(key, out type) |
|---|
| 833 | return type to ! |
|---|
| 834 | else |
|---|
| 835 | type = _intTypes[key] = IntType(signed, size, .anyIntType) |
|---|
| 836 | return type to ! |
|---|
| 837 | |
|---|
| 838 | get nilType as NilType |
|---|
| 839 | if _nilType is nil |
|---|
| 840 | _nilType = NilType() |
|---|
| 841 | return _nilType to ! |
|---|
| 842 | |
|---|
| 843 | get numberType as AbstractNumberType |
|---|
| 844 | if _numberType is nil |
|---|
| 845 | branch .options['number'] to String |
|---|
| 846 | on 'decimal', _numberType = .decimalType |
|---|
| 847 | on 'float', _numberType = .floatType |
|---|
| 848 | on 'float32', _numberType = .floatType(32) |
|---|
| 849 | on 'float64', _numberType = .floatType(64) |
|---|
| 850 | else, throw FallThroughException(.options['number']) |
|---|
| 851 | return _numberType to ! |
|---|
| 852 | |
|---|
| 853 | set numberTypeName as String |
|---|
| 854 | """ |
|---|
| 855 | Set `number` type from a string/name. |
|---|
| 856 | """ |
|---|
| 857 | require value in ['decimal', 'float', 'float32', 'float64'] |
|---|
| 858 | branch value |
|---|
| 859 | on 'decimal', _numberType = .decimalType |
|---|
| 860 | on 'float', _numberType = .floatType |
|---|
| 861 | on 'float32', _numberType = .floatType(32) |
|---|
| 862 | on 'float64', _numberType = .floatType(64) |
|---|
| 863 | else, throw FallThroughException(value) |
|---|
| 864 | |
|---|
| 865 | get passThroughType as PassThroughType |
|---|
| 866 | if _passThroughType is nil |
|---|
| 867 | _passThroughType = PassThroughType() |
|---|
| 868 | return _passThroughType to ! |
|---|
| 869 | |
|---|
| 870 | get unspecifiedType as UnspecifiedType |
|---|
| 871 | if _unspecifiedType is nil |
|---|
| 872 | _unspecifiedType = UnspecifiedType() |
|---|
| 873 | return _unspecifiedType to ! |
|---|
| 874 | |
|---|
| 875 | get voidType as VoidType |
|---|
| 876 | if _voidType is nil |
|---|
| 877 | _voidType = VoidType() |
|---|
| 878 | return _voidType to ! |
|---|
| 879 | |
|---|
| 880 | def nilableType(type as IType) as NilableType |
|---|
| 881 | if type inherits NilableType, return type |
|---|
| 882 | if _nilableTypes is nil |
|---|
| 883 | _nilableTypes = Dictionary<of INode, NilableType>() |
|---|
| 884 | if _nilableTypes.containsKey(type) |
|---|
| 885 | return _nilableTypes[type] |
|---|
| 886 | else |
|---|
| 887 | _nilableTypes[type] = nt = NilableType(type) |
|---|
| 888 | nt.bindInh |
|---|
| 889 | if not .isBindingInh, nt.bindInt |
|---|
| 890 | return nt |
|---|
| 891 | |
|---|
| 892 | def variType(type as IType) as VariType |
|---|
| 893 | if _variTypes is nil |
|---|
| 894 | _variTypes = Dictionary<of INode, VariType>() |
|---|
| 895 | else if _variTypes.containsKey(type) |
|---|
| 896 | return _variTypes[type] |
|---|
| 897 | _variTypes[type] = vt = VariType(type) |
|---|
| 898 | return vt |
|---|
| 899 | |
|---|
| 900 | def defaultType as IType |
|---|
| 901 | return .nilableDynamicType |
|---|
| 902 | |
|---|
| 903 | def nilableDynamicType as NilableType |
|---|
| 904 | if _nilableDynamicType is nil |
|---|
| 905 | _nilableDynamicType = .nilableType(.dynamicType) |
|---|
| 906 | return _nilableDynamicType to ! |
|---|
| 907 | # -- end of ITypeProvider |
|---|
| 908 | |
|---|
| 909 | |
|---|
| 910 | |
|---|
| 911 | get typeType as IType |
|---|
| 912 | return .libraryType('Type') |
|---|
| 913 | |
|---|
| 914 | ## More type stuff |
|---|
| 915 | |
|---|
| 916 | def readSystemTypes |
|---|
| 917 | .backEnd.readSystemTypes |
|---|
| 918 | |
|---|
| 919 | def fixLibExtensions(references as List<of String>) |
|---|
| 920 | i = 0 |
|---|
| 921 | for reference in List<of String>(references) |
|---|
| 922 | references[i] = .backEnd.fixLibExtension(references[i]) |
|---|
| 923 | i += 1 |
|---|
| 924 | |
|---|
| 925 | def printRefs(references as List<of String>) |
|---|
| 926 | if references.count == 0 |
|---|
| 927 | print 'No additional assembly references.' |
|---|
| 928 | else |
|---|
| 929 | print 'Final assembly reference list:' |
|---|
| 930 | i = 0 |
|---|
| 931 | for refPath in references |
|---|
| 932 | print '[i]. [refPath]' |
|---|
| 933 | i += 1 |
|---|
| 934 | |
|---|
| 935 | def readAssemblyTypes(options as OptionValues) |
|---|
| 936 | references = options.getStringList('reference') |
|---|
| 937 | |
|---|
| 938 | # Excluding the extension can be problematic |
|---|
| 939 | .fixLibExtensions(references) |
|---|
| 940 | |
|---|
| 941 | # now that references are fixed, make a copy so that .options['references'] is not modified further |
|---|
| 942 | references = List<of String>(references) |
|---|
| 943 | |
|---|
| 944 | if options.containsKey('pkg') |
|---|
| 945 | for pkgName in options['pkg'] to List<of String> |
|---|
| 946 | references.addRange(.refsForPackage(pkgName)) |
|---|
| 947 | |
|---|
| 948 | if .verbosity > 1 |
|---|
| 949 | .printRefs(references) |
|---|
| 950 | |
|---|
| 951 | # Here be "reflectionOnlyLoad" code... which does not work on Mono 1.2.4 |
|---|
| 952 | # The run-time error message says: |
|---|
| 953 | # ** (./cobra.exe:24284): WARNING **: Cannot resolve dependency to assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event. |
|---|
| 954 | # But hooking ReflectionOnlyAssemblyResolve has no effect as it never gets called. |
|---|
| 955 | # Looks like others are having problems too: |
|---|
| 956 | # http://csammisrun.net/shaim/viewtopic.php?t=29&sid=e46dc962b1e4d14a5210ae2852ac7d87 |
|---|
| 957 | # General references: |
|---|
| 958 | # * "Reflection Only Assembly Loading" blog entry by Junfeng Zhang |
|---|
| 959 | # http://blogs.msdn.com/junfeng/archive/2004/08/24/219691.aspx |
|---|
| 960 | # * http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2485940&SiteID=1 |
|---|
| 961 | # Justin Pinnix from MSFT says "Once our assembly resolver finds your DLL, it calls |
|---|
| 962 | # Assembly.LoadFrom(). So, even though your code calls Assembly.Load, we still |
|---|
| 963 | # call Assembly.LoadFrom" |
|---|
| 964 | # TODO: try this out on MS .NET and if it works there, use it there |
|---|
| 965 | # TODO: file a Mono bug report (or ping an existing one) |
|---|
| 966 | # curDomain = AppDomain.currentDomain |
|---|
| 967 | # sharp'curDomain.ReflectionOnlyAssemblyResolve += _resolveEvent' |
|---|
| 968 | |
|---|
| 969 | for reference in references |
|---|
| 970 | if not .loadReference(reference, false) |
|---|
| 971 | _addMessage(SourceException('Cannot locate assembly reference "[reference]".')) |
|---|
| 972 | |
|---|
| 973 | def refsForPackage(pkgName as String) as List<of String> |
|---|
| 974 | """ |
|---|
| 975 | Returns the library/DLL refs for the give package name. |
|---|
| 976 | Runs pkg-config to get that list. |
|---|
| 977 | See HowTo/GTK.cobra |
|---|
| 978 | """ |
|---|
| 979 | refs = List<of String>() |
|---|
| 980 | |
|---|
| 981 | # example: pkg-config --libs gtk-sharp-2.0 |
|---|
| 982 | p = Process() |
|---|
| 983 | macMonoPkgConfig = '/Library/Frameworks/Mono.framework/Commands/pkg-config' |
|---|
| 984 | if File.exists(macMonoPkgConfig) |
|---|
| 985 | # avoid conflict with MacPorts whose pkg-config in '/opt/local/bin/pkg-config' will not pick up on Mono packages |
|---|
| 986 | p.startInfo.fileName = macMonoPkgConfig |
|---|
| 987 | else |
|---|
| 988 | p.startInfo.fileName = 'pkg-config' |
|---|
| 989 | p.startInfo.arguments = '--libs [pkgName]' |
|---|
| 990 | if .verbosity > 1 |
|---|
| 991 | print 'Running: [p.startInfo.fileName] [p.startInfo.arguments]' |
|---|
| 992 | output = CobraCore.runAndCaptureAllOutput(p).trim |
|---|
| 993 | if p.exitCode |
|---|
| 994 | _addMessage(SourceException('Cannot locate package "[pkgName]"')) |
|---|
| 995 | print ' $ [p.startInfo.fileName] [p.startInfo.arguments]' |
|---|
| 996 | for line in output.split(c'\n') |
|---|
| 997 | print ' |', line |
|---|
| 998 | return refs |
|---|
| 999 | |
|---|
| 1000 | parts = output.replace('-r:', '\0').split(c'\0') # while Cobra uses -ref:, pkg-config outputs -r: |
|---|
| 1001 | for part in parts |
|---|
| 1002 | if part.trim <> '', refs.add(part.trim) |
|---|
| 1003 | |
|---|
| 1004 | return refs |
|---|
| 1005 | |
|---|
| 1006 | def _resolveEvent(sender as Object, args as ResolveEventArgs) as Assembly? |
|---|
| 1007 | return nil |
|---|
| 1008 | |
|---|
| 1009 | var _referenceVerbosity = -1 |
|---|
| 1010 | |
|---|
| 1011 | get referenceVerbosity as int |
|---|
| 1012 | if _referenceVerbosity == -1 |
|---|
| 1013 | _referenceVerbosity = .options.getDefault('verbosity-ref', 0) |
|---|
| 1014 | return _referenceVerbosity |
|---|
| 1015 | |
|---|
| 1016 | var _willReadDependencies = false |
|---|
| 1017 | |
|---|
| 1018 | def indentPrint |
|---|
| 1019 | # see .loadReference which sets up the indented writer |
|---|
| 1020 | (CobraCore.printDestination to IndentedWriter).indent |
|---|
| 1021 | |
|---|
| 1022 | def outdentPrint |
|---|
| 1023 | # see .loadReference which sets up the indented writer |
|---|
| 1024 | (CobraCore.printDestination to IndentedWriter).dedent |
|---|
| 1025 | |
|---|
| 1026 | def loadReference(reference as String, addExtn as bool) as bool |
|---|
| 1027 | """ |
|---|
| 1028 | Attempts to load the given library reference. |
|---|
| 1029 | On success, adds reference to .loadedReferences and returns true. |
|---|
| 1030 | On failure, returns false, but does not add any warnings or errors. |
|---|
| 1031 | This method is used by Compiler and UseDirective. |
|---|
| 1032 | """ |
|---|
| 1033 | if addExtn |
|---|
| 1034 | reference = .backEnd.fixLibExtension(reference) |
|---|
| 1035 | v, rv = .verbosity, .referenceVerbosity |
|---|
| 1036 | if v, print 'Loading reference:', reference |
|---|
| 1037 | if rv |
|---|
| 1038 | print to IndentedWriter(CobraCore.printDestination, indentString=' : ') |
|---|
| 1039 | print '>> .loadReference("[reference]")' |
|---|
| 1040 | .indentPrint |
|---|
| 1041 | try |
|---|
| 1042 | r = _loadReference(reference) |
|---|
| 1043 | finally |
|---|
| 1044 | print 'Returning [r]' |
|---|
| 1045 | .outdentPrint |
|---|
| 1046 | print '<< .loadReference("[reference]")' |
|---|
| 1047 | else |
|---|
| 1048 | r = _loadReference(reference) |
|---|
| 1049 | if v and not r, print 'Could not load: ', reference |
|---|
| 1050 | return r |
|---|
| 1051 | |
|---|
| 1052 | def _loadReference(reference as String) as bool |
|---|
| 1053 | try |
|---|
| 1054 | return .backEnd.loadLibReference(reference) |
|---|
| 1055 | catch lre as LoadReferenceException |
|---|
| 1056 | # example: 'FileNotFoundException fileName Could not load file or assembly 'NHibernate, Version=2.1.0.1001, |
|---|
| 1057 | # Culture=neutral, PublcKeyToken=aa95f207798dfdb4' or one of its dependencies. The |
|---|
| 1058 | # system cannot find the file specified. |
|---|
| 1059 | if .referenceVerbosity, print '[lre] in _loadReference("[reference]")' |
|---|
| 1060 | return false |
|---|
| 1061 | |
|---|
| 1062 | def fixMemberSigs |
|---|
| 1063 | .backEnd.fixMemberSigs |
|---|
| 1064 | |
|---|
| 1065 | def installNativeMethods(box as Box, nativeType as NativeType) |
|---|
| 1066 | .backEnd.installNativeMethods(box, nativeType) |
|---|
| 1067 | |
|---|
| 1068 | ## |
|---|
| 1069 | ## Binding |
|---|
| 1070 | ## |
|---|
| 1071 | |
|---|
| 1072 | var _basicTypes as List<of PrimitiveType>? |
|---|
| 1073 | |
|---|
| 1074 | get basicTypes as IList<of PrimitiveType> |
|---|
| 1075 | if _basicTypes is nil |
|---|
| 1076 | _basicTypes = List<of PrimitiveType>() |
|---|
| 1077 | _basicTypes.addRange([.anyFloatType, .anyIntType, .boolType, .charType, .decimalType, .floatType]) |
|---|
| 1078 | for size in [32, 64] |
|---|
| 1079 | _basicTypes.add(.floatType(size)) |
|---|
| 1080 | for signed in [true, false] |
|---|
| 1081 | for size in [8, 16, 32, 64] |
|---|
| 1082 | _basicTypes.add(.intType(signed, size)) |
|---|
| 1083 | return _basicTypes to ! |
|---|
| 1084 | |
|---|
| 1085 | def symbolForName(name as String, haveThis as bool) as IMember? |
|---|
| 1086 | return .symbolForName(name, haveThis, false) |
|---|
| 1087 | |
|---|
| 1088 | def symbolForName(name as String, haveThis as bool, isLowerOkay as bool) as IMember? |
|---|
| 1089 | """ |
|---|
| 1090 | name - obvious. |
|---|
| 1091 | haveThis - if false, symbols like methods, properties, etc. will not be returned while enums, nested classes, etc. could be. |
|---|
| 1092 | isLowerOkay - true if a lowercase name (non capCased) is allowed. |
|---|
| 1093 | """ |
|---|
| 1094 | require |
|---|
| 1095 | name.length |
|---|
| 1096 | body |
|---|
| 1097 | # TODO: remove canBeMember arg |
|---|
| 1098 | assert _curModule |
|---|
| 1099 | |
|---|
| 1100 | # check for generic parameters in methods |
|---|
| 1101 | if not haveThis and not name[0].isLower |
|---|
| 1102 | if _codeMemberStack.count and _codeMemberStack.peek inherits Method |
|---|
| 1103 | m = (_codeMemberStack.peek to Method).genericParamForName(name) to IMember? |
|---|
| 1104 | if m, return m |
|---|
| 1105 | |
|---|
| 1106 | # check the current box which will ask its namespace which will ask its `use` directives |
|---|
| 1107 | if _boxStack.count |
|---|
| 1108 | #print '>> .compiler.symbolForName([name], [haveThis])' |
|---|
| 1109 | if not isLowerOkay and name[0].isLower |
|---|
| 1110 | assert false, 'use findLocal instead. [name]' |
|---|
| 1111 | return nil |
|---|
| 1112 | m = _boxStack.peek.symbolForName(name, haveThis) |
|---|
| 1113 | #print '<< .compiler.symbolForName returning', m |
|---|
| 1114 | #print |
|---|
| 1115 | |
|---|
| 1116 | if m is nil |
|---|
| 1117 | # global namespace of the current module |
|---|
| 1118 | m = ((_curModule to dynamic).topNameSpace).symbolForName(name) |
|---|
| 1119 | # TODO: cast above is kind of weird, but SharpModule has no .topNameSpace |
|---|
| 1120 | |
|---|
| 1121 | return m |
|---|
| 1122 | |
|---|
| 1123 | def dumpModulesForTestify(output as IndentedWriter) |
|---|
| 1124 | .dumpModulesForTestify(output, 'Modules') |
|---|
| 1125 | |
|---|
| 1126 | def dumpModulesForTestify(output as IndentedWriter, title as String) |
|---|
| 1127 | print '[title]:' |
|---|
| 1128 | output.indent |
|---|
| 1129 | try |
|---|
| 1130 | print to output |
|---|
| 1131 | i = 1 |
|---|
| 1132 | for module in _modules |
|---|
| 1133 | print '[i]. ' stop |
|---|
| 1134 | if module.isCobraLibrary |
|---|
| 1135 | print to output, module |
|---|
| 1136 | else |
|---|
| 1137 | module.writeDeepString(output) |
|---|
| 1138 | i += 1 |
|---|
| 1139 | finally |
|---|
| 1140 | output.dedent |
|---|
| 1141 | |
|---|
| 1142 | def _exitFromErrors |
|---|
| 1143 | # TODO 2009-12 Having problems with this requirement in same cases. Not important to enforce right now. |
|---|
| 1144 | # require .errors.count |
|---|
| 1145 | """ |
|---|
| 1146 | This method should not be invoked from partial classes found in phases or back-ends. Some |
|---|
| 1147 | phases want to run even when there are errors from previous phases. This is managed by |
|---|
| 1148 | the compiler's execution of the phases. |
|---|
| 1149 | """ |
|---|
| 1150 | .printMessages |
|---|
| 1151 | _deleteIntermediateFiles |
|---|
| 1152 | throw StopCompilation(this) |
|---|
| 1153 | |
|---|
| 1154 | def printMessages |
|---|
| 1155 | if .htmlWriter |
|---|
| 1156 | .printHtmlMessages |
|---|
| 1157 | else |
|---|
| 1158 | .printConsoleMessages |
|---|
| 1159 | Environment.exitCode = if(.errors.count, 1, 0) |
|---|
| 1160 | |
|---|
| 1161 | def printHtmlMessages |
|---|
| 1162 | require .htmlWriter |
|---|
| 1163 | dest = .htmlWriter to ! |
|---|
| 1164 | dest.writeHtml('<table class=messages cellpadding=2 cellspacing=2>[dest.newLine]\n') |
|---|
| 1165 | dest.writeHtml('<tr class=headings> <td class=heading> File </td> <td class=heading> Line </td> <td class=heading> Type </td> <td class=heading> Message </td> </tr>\n') |
|---|
| 1166 | for msg in _messages |
|---|
| 1167 | msg.writeHtmlTo(dest) |
|---|
| 1168 | dest.writeHtml('</table>[dest.newLine]') |
|---|
| 1169 | if _errors.count |
|---|
| 1170 | dest.writeHtml('<span class=compilationFailed>[_compilationFailedMessage()]</span><br>\n') # CC: axe ()s |
|---|
| 1171 | else |
|---|
| 1172 | dest.writeHtml('<span class=compilationSucceeded>Compilation succeeded - [_warnings.count] warning[if(_warnings.count==1,'','s')]</span><br>\n') |
|---|
| 1173 | |
|---|
| 1174 | def printConsoleMessages |
|---|
| 1175 | willColor = .options.boolValue('color') |
|---|
| 1176 | savedColor = Console.foregroundColor |
|---|
| 1177 | if CobraCore.isRunningOnMono # bug on mono: the initial Console.foregroundColor is not necessarily accurate (at least in bash on Terminal.app on Mac OS X 10.4.10 on Mono 1.2.4) |
|---|
| 1178 | savedColor = ConsoleColor.Black # this is really just a guess and possibly a bad one. should be controllable via a cmd line option or argument |
|---|
| 1179 | dest = if(Console.error, Console.error, Console.out) to ! # some IDEs prefer errors and warnings to go to stderr |
|---|
| 1180 | for obj in _messages |
|---|
| 1181 | if willColor and obj.isError |
|---|
| 1182 | Console.foregroundColor = ConsoleColor.Red |
|---|
| 1183 | restoreColor = true |
|---|
| 1184 | print to dest, obj.consoleString |
|---|
| 1185 | if restoreColor |
|---|
| 1186 | Console.foregroundColor = savedColor |
|---|
| 1187 | restoreColor = false |
|---|
| 1188 | if _errors.count |
|---|
| 1189 | if willColor, Console.foregroundColor = ConsoleColor.Red |
|---|
| 1190 | print _compilationFailedMessage() # CC: axe ()s |
|---|
| 1191 | if willColor, Console.foregroundColor = savedColor |
|---|
| 1192 | else |
|---|
| 1193 | didPrint = false |
|---|
| 1194 | if willColor, Console.foregroundColor = ConsoleColor.Blue |
|---|
| 1195 | if _willPrintSuccessMsg or _warnings.count |
|---|
| 1196 | print 'Compilation succeeded' stop |
|---|
| 1197 | didPrint = true |
|---|
| 1198 | if _warnings.count |
|---|
| 1199 | print ' - [_warnings.count] warning[if(_warnings.count==1,'','s')]' |
|---|
| 1200 | else |
|---|
| 1201 | if didPrint, print |
|---|
| 1202 | if willColor, Console.foregroundColor = savedColor |
|---|
| 1203 | |
|---|
| 1204 | def _compilationFailedMessage as String |
|---|
| 1205 | return 'Compilation failed - [_errors.count] error[if(_errors.count==1,'','s')], [_warnings.count] warning[if(_warnings.count==1,'','s')]' |
|---|
| 1206 | |
|---|
| 1207 | def printTimingStats(elapsed as TimeSpan) |
|---|
| 1208 | secs = elapsed.totalMilliseconds / 1_000 |
|---|
| 1209 | if .linesCompiled |
|---|
| 1210 | lps = .linesCompiled / secs |
|---|
| 1211 | print '[.linesCompiled] lines compiled at [lps:F1] lines/sec' |
|---|
| 1212 | if .nodesCompiled |
|---|
| 1213 | nps = .nodesCompiled / secs |
|---|
| 1214 | print '[.nodesCompiled] nodes compiled at [nps:F1] nodes/sec' |
|---|
| 1215 | if .tokensCompiled |
|---|
| 1216 | tps = .tokensCompiled / secs |
|---|
| 1217 | print '[.tokensCompiled] tokens compiled at [tps:F1] tokens/sec' |
|---|
| 1218 | |
|---|
| 1219 | ## |
|---|
| 1220 | ## Source Code Corrections |
|---|
| 1221 | ## |
|---|
| 1222 | |
|---|
| 1223 | var _sourceCorrections = Dictionary<of String, Dictionary<of int, List<of Replacement>>>() |
|---|
| 1224 | """ |
|---|
| 1225 | The first key of type String is filename. |
|---|
| 1226 | The second key of type int is line number. |
|---|
| 1227 | """ |
|---|
| 1228 | |
|---|
| 1229 | def correctSource(token as IToken, replace as String) |
|---|
| 1230 | if not _sourceCorrections.containsKey(token.fileName) |
|---|
| 1231 | _sourceCorrections[token.fileName] = Dictionary<of int, List<of Replacement>>() |
|---|
| 1232 | file = _sourceCorrections[token.fileName] |
|---|
| 1233 | if not file.containsKey(token.lineNum) |
|---|
| 1234 | file[token.lineNum] = List<of Replacement>() |
|---|
| 1235 | file[token.lineNum].add(Replacement(token.colNum, token.text, replace)) |
|---|
| 1236 | |
|---|
| 1237 | def writeSourceCodeCorrections |
|---|
| 1238 | # TODO: this doesn't properly handle when the replacements are a different length than the |
|---|
| 1239 | # original next, but then this isn't needed right now since the only type of correction |
|---|
| 1240 | # being done is case correction |
|---|
| 1241 | if _sourceCorrections.count == 0, return |
|---|
| 1242 | v = .verbosity |
|---|
| 1243 | for fileName, lineToReplacements in _sourceCorrections |
|---|
| 1244 | if v, print 'Correcting source:', fileName |
|---|
| 1245 | lineNums = List<of int>(lineToReplacements.keys) |
|---|
| 1246 | lineNums.sort |
|---|
| 1247 | lines = File.readAllLines(fileName) |
|---|
| 1248 | for lineNum in lineNums |
|---|
| 1249 | for rep in lineToReplacements[lineNum] |
|---|
| 1250 | assert rep.oldText.length == rep.newText.length # see TODO above |
|---|
| 1251 | if v >= 2, print 'Correct: Line [lineNum], Column [rep.colNum], Old "[rep.oldText]", New "[rep.newText]"' |
|---|
| 1252 | i = lineNum - 1 |
|---|
| 1253 | line = lines[i] |
|---|
| 1254 | # print line |
|---|
| 1255 | oldLen = line.length |
|---|
| 1256 | col = rep.colNum - 1 |
|---|
| 1257 | lines[i] = line[:col] + rep.newText + line[col+rep.oldText.length:] |
|---|
| 1258 | # print lines[i] |
|---|
| 1259 | assert lines[i].length == oldLen |
|---|
| 1260 | try |
|---|
| 1261 | File.writeAllLines(fileName, lines) |
|---|
| 1262 | catch exc as Exception |
|---|
| 1263 | .warning(CobraWarning(fileName, nil, 'Cannot write source code corrections due to: [exc.message] ([exc.getType.name]).')) |
|---|
| 1264 | _sourceCorrections.clear |
|---|
| 1265 | |
|---|
| 1266 | ## |
|---|
| 1267 | ## Important system library types |
|---|
| 1268 | ## |
|---|
| 1269 | # ILibTypeProvider |
|---|
| 1270 | |
|---|
| 1271 | get objectType as IType |
|---|
| 1272 | # for ITypeProvider |
|---|
| 1273 | return _libraryClass('Object') |
|---|
| 1274 | |
|---|
| 1275 | def objectClass as Class |
|---|
| 1276 | # for stronger typing |
|---|
| 1277 | return _libraryClass('Object') |
|---|
| 1278 | |
|---|
| 1279 | def stringType as Class |
|---|
| 1280 | return _libraryClass('String') |
|---|
| 1281 | |
|---|
| 1282 | def exceptionType as Class |
|---|
| 1283 | return _libraryClass('Exception') |
|---|
| 1284 | |
|---|
| 1285 | def delegateType as Class |
|---|
| 1286 | return _libraryClass('Delegate') |
|---|
| 1287 | |
|---|
| 1288 | def attributeType as Box |
|---|
| 1289 | return _libraryBox('Attribute') |
|---|
| 1290 | |
|---|
| 1291 | def enumerableType as Box |
|---|
| 1292 | return _libraryBox('IEnumerable') |
|---|
| 1293 | |
|---|
| 1294 | def enumeratorType as Box |
|---|
| 1295 | return _libraryBox('IEnumerator') |
|---|
| 1296 | |
|---|
| 1297 | def enumerableOfType as Box |
|---|
| 1298 | return _libraryBox('IEnumerable<of>') |
|---|
| 1299 | |
|---|
| 1300 | def enumeratorOfType as Box |
|---|
| 1301 | return _libraryBox('IEnumerator<of>') |
|---|
| 1302 | |
|---|
| 1303 | def dictEnumeratorType as Box |
|---|
| 1304 | return _libraryBox('IDictionaryEnumerator') |
|---|
| 1305 | |
|---|
| 1306 | def collectionType as Box |
|---|
| 1307 | return _libraryBox('ICollection') |
|---|
| 1308 | |
|---|
| 1309 | def collectionOfType as Box |
|---|
| 1310 | return _libraryBox('ICollection<of>') |
|---|
| 1311 | |
|---|
| 1312 | def ilistType as Box |
|---|
| 1313 | return _libraryBox('IList') |
|---|
| 1314 | |
|---|
| 1315 | def ilistOfType as Box |
|---|
| 1316 | return _libraryBox('IList<of>') |
|---|
| 1317 | |
|---|
| 1318 | def listOfType as Class |
|---|
| 1319 | return _libraryClass('List<of>') |
|---|
| 1320 | |
|---|
| 1321 | def idictionaryType as Box |
|---|
| 1322 | return _libraryBox('IDictionary') |
|---|
| 1323 | |
|---|
| 1324 | def idictionaryOfType as Box |
|---|
| 1325 | return _libraryBox('IDictionary<of,>') |
|---|
| 1326 | |
|---|
| 1327 | def dictionaryOfType as Class |
|---|
| 1328 | return _libraryClass('Dictionary<of,>') |
|---|
| 1329 | |
|---|
| 1330 | def setOfType as Class |
|---|
| 1331 | return _libraryClass('Set<of>') |
|---|
| 1332 | |
|---|
| 1333 | def libraryType(qualifiedNameOrTag as String) as IType |
|---|
| 1334 | """ |
|---|
| 1335 | Implemented for ITypeProvider, but use the more specific methods such as .stringType instead. |
|---|
| 1336 | """ |
|---|
| 1337 | return _libraryType(.backEnd.resolveTypeTag(qualifiedNameOrTag)) |
|---|
| 1338 | |
|---|
| 1339 | var _libraryTypeCache = Dictionary<of String, IType>() |
|---|
| 1340 | |
|---|
| 1341 | def _libraryType(qualifiedName as String) as IType |
|---|
| 1342 | """ |
|---|
| 1343 | find qualified Type name in (cache or) Library/namespace Decls structures. |
|---|
| 1344 | Used to retrieve types such as System.String. |
|---|
| 1345 | Example: |
|---|
| 1346 | _libraryType('System.String') |
|---|
| 1347 | """ |
|---|
| 1348 | assert qualifiedName.length |
|---|
| 1349 | type as IType? |
|---|
| 1350 | if _libraryTypeCache.tryGetValue(qualifiedName, out type) |
|---|
| 1351 | return type to ! |
|---|
| 1352 | names = qualifiedName.split(c'.') |
|---|
| 1353 | type = _libraryType(names to passthrough) |
|---|
| 1354 | _libraryTypeCache[qualifiedName] = type to ! |
|---|
| 1355 | return type to ! |
|---|
| 1356 | |
|---|
| 1357 | def _libraryType(names as vari String) as IType |
|---|
| 1358 | #trace names |
|---|
| 1359 | ns as IContainer = _globalNS |
|---|
| 1360 | thing as IContainer? = nil |
|---|
| 1361 | for name in names |
|---|
| 1362 | possible = (thing ? ns).declForName(name) |
|---|
| 1363 | #print name |
|---|
| 1364 | # dbg |
|---|
| 1365 | #if not possible |
|---|
| 1366 | # trace thing ? ns |
|---|
| 1367 | # ((thing ? ns) to NameSpace).dumpDeclsNameKeys |
|---|
| 1368 | assert possible, name |
|---|
| 1369 | if possible inherits IContainer |
|---|
| 1370 | thing = possible |
|---|
| 1371 | else |
|---|
| 1372 | assert false, [name, possible.getType.name, possible] |
|---|
| 1373 | if thing inherits IType |
|---|
| 1374 | if thing.name<>names[names.length-1] # TODO: add this as an ensure as well |
|---|
| 1375 | print 'names=' stop |
|---|
| 1376 | print CobraCore.toTechString(names) |
|---|
| 1377 | print 'thing=[thing]' |
|---|
| 1378 | assert false |
|---|
| 1379 | return thing |
|---|
| 1380 | else |
|---|
| 1381 | throw FallThroughException('found [name], but it is not an IType. it is [thing]') |
|---|
| 1382 | |
|---|
| 1383 | def _libraryBox(qualifiedNameOrTag as String) as Box |
|---|
| 1384 | """ |
|---|
| 1385 | Returns a box from the standard library such as 'System.Object' or 'System.Collections.Generic.IEnumerable<of>'. |
|---|
| 1386 | """ |
|---|
| 1387 | return _libraryType(.backEnd.resolveTypeTag(qualifiedNameOrTag)) to Box |
|---|
| 1388 | |
|---|
| 1389 | def _libraryClass(qualifiedNameOrTag as String) as Class |
|---|
| 1390 | """ |
|---|
| 1391 | Returns a class from the standard library such as 'System.Object' or 'System.Collections.Generic.Dictionary<of,>'. |
|---|
| 1392 | """ |
|---|
| 1393 | return _libraryType(.backEnd.resolveTypeTag(qualifiedNameOrTag)) to Class |
|---|
| 1394 | |
|---|
| 1395 | |
|---|
| 1396 | ## |
|---|
| 1397 | ## Services to nodes |
|---|
| 1398 | ## |
|---|
| 1399 | |
|---|
| 1400 | def embedRunTimeSuffix as String |
|---|
| 1401 | return .options.embedRunTimeSuffix |
|---|
| 1402 | |
|---|
| 1403 | #special type proxies for common Types - Object and Type |
|---|
| 1404 | def objectTypeProxy as AbstractTypeProxy |
|---|
| 1405 | """ |
|---|
| 1406 | Type proxy for BE notion of a class for the root of Object hierarchy |
|---|
| 1407 | e.g. Object in dotNet |
|---|
| 1408 | """ |
|---|
| 1409 | #return ClrTypeProxy(Object) |
|---|
| 1410 | return .backEnd.objectTypeProxy |
|---|
| 1411 | |
|---|
| 1412 | def typeTypeProxy as AbstractTypeProxy |
|---|
| 1413 | """ |
|---|
| 1414 | Type proxy for BE notion of a class for a Type |
|---|
| 1415 | e.g. System.Type in dotNet, java.lang.class in Java |
|---|
| 1416 | """ |
|---|
| 1417 | #return ClrTypeProxy(Type) |
|---|
| 1418 | return .backEnd.typeTypeProxy |
|---|
| 1419 | |
|---|
| 1420 | def nativeType(qualifiedName as String) as NativeType |
|---|
| 1421 | return .backEnd.nativeTypeByName(qualifiedName) |
|---|
| 1422 | |
|---|
| 1423 | def suggestionFor(name as String) as String? is shared |
|---|
| 1424 | require name.length |
|---|
| 1425 | # CC: return _unknownSuggestions.getDefault(name, nil) |
|---|
| 1426 | if _unknownSuggestions.containsKey(name) |
|---|
| 1427 | return _unknownSuggestions[name] |
|---|
| 1428 | else if _unknownSuggestions.containsKey(name.toLower) |
|---|
| 1429 | return _unknownSuggestions[name.toLower] |
|---|
| 1430 | else |
|---|
| 1431 | return nil |
|---|
| 1432 | |
|---|
| 1433 | def warning(node as ISyntaxNode, msg as String) |
|---|
| 1434 | require msg.length |
|---|
| 1435 | .warning(CobraWarning(node.token, msg)) |
|---|
| 1436 | |
|---|
| 1437 | def warning(cw as CobraWarning) |
|---|
| 1438 | require not cw.isError |
|---|
| 1439 | if _suppressWarning(cw), return |
|---|
| 1440 | _addMessage(cw) |
|---|
| 1441 | |
|---|
| 1442 | def _suppressWarning(cw as CobraWarning) as bool |
|---|
| 1443 | # TODO: add suppress all warnings (cmdline sw) - maybe? |
|---|
| 1444 | # TODO: add suppress by error tag - maybe? |
|---|
| 1445 | entry = if(cw.hasSourceSite, '[cw.fileName]:[cw.lineNum]', '') |
|---|
| 1446 | return _noWarningLines.contains(entry) |
|---|
| 1447 | |
|---|
| 1448 | def augmentWarning(node as ISyntaxNode, lookFor as String, search as String, augment as String) as bool |
|---|
| 1449 | require |
|---|
| 1450 | lookFor.length |
|---|
| 1451 | augment.length |
|---|
| 1452 | body |
|---|
| 1453 | message = CobraWarning(node.token, lookFor) |
|---|
| 1454 | key = if(message.hasSourceSite, '[message.fileName]:[message.lineNum]', '') |
|---|
| 1455 | if not _messagesPerSourceLine.containsKey(key) |
|---|
| 1456 | return false |
|---|
| 1457 | lowerToMatch = message.message.toLower |
|---|
| 1458 | lowerSearch = if(search.length > 0, search.toLower, '') |
|---|
| 1459 | for se in _messagesPerSourceLine[key] |
|---|
| 1460 | lowerMsg = se.message.toLower |
|---|
| 1461 | if lowerMsg.contains(lowerToMatch) # matched |
|---|
| 1462 | if search.length == 0 or lowerMsg.contains(lowerSearch) |
|---|
| 1463 | se.appendToMessage(augment) |
|---|
| 1464 | return true |
|---|
| 1465 | return false |
|---|
| 1466 | |
|---|
| 1467 | shared |
|---|
| 1468 | var _unknownSuggestions = { |
|---|
| 1469 | # literals |
|---|
| 1470 | 'null': 'nil', |
|---|
| 1471 | 'none': 'nil', |
|---|
| 1472 | 'nothing': 'nil', |
|---|
| 1473 | 'True': 'true', |
|---|
| 1474 | 'False': 'false', |
|---|
| 1475 | |
|---|
| 1476 | # Python |
|---|
| 1477 | 'self': 'this', |
|---|
| 1478 | 'super': 'base', |
|---|
| 1479 | 'elif': 'else if', |
|---|
| 1480 | |
|---|
| 1481 | # Ruby |
|---|
| 1482 | 'puts': 'print', |
|---|
| 1483 | |
|---|
| 1484 | # C# |
|---|
| 1485 | 'using': 'use', |
|---|
| 1486 | 'foreach': 'for', |
|---|
| 1487 | |
|---|
| 1488 | # VisualBasic |
|---|
| 1489 | 'imports': 'use', |
|---|
| 1490 | 'sub': 'def', |
|---|
| 1491 | |
|---|
| 1492 | # operators (word ones anyway) |
|---|
| 1493 | 'isa': 'inherits', |
|---|
| 1494 | 'new': 'SomeClass() or SomeClass(arg1,arg2) without new', |
|---|
| 1495 | |
|---|
| 1496 | # constructs |
|---|
| 1497 | #'lambda': 'def(args) ...', # TODO |
|---|
| 1498 | |
|---|
| 1499 | # statements |
|---|
| 1500 | 'do': 'post while <condition>', |
|---|
| 1501 | 'elseif': 'else if', |
|---|
| 1502 | |
|---|
| 1503 | # types |
|---|
| 1504 | 'boolean': 'bool', |
|---|
| 1505 | 'character': 'char', |
|---|
| 1506 | 'byte': 'uint8', |
|---|
| 1507 | 'sbyte': 'int8', |
|---|
| 1508 | 'short': 'int16', |
|---|
| 1509 | 'ushort': 'uint16', |
|---|
| 1510 | 'integer': 'int', |
|---|
| 1511 | 'long': 'int64', |
|---|
| 1512 | 'ulong': 'uint64', |
|---|
| 1513 | 'single': 'float32', |
|---|
| 1514 | 'double': 'float', |
|---|
| 1515 | 'str': 'String', |
|---|
| 1516 | 'string': 'String', |
|---|
| 1517 | 'object': 'Object', |
|---|
| 1518 | } |
|---|
| 1519 | |
|---|
| 1520 | get unknownSuggestions from var |
|---|
| 1521 | |
|---|
| 1522 | def suggestionForUnknown(word as String) as String? |
|---|
| 1523 | if _unknownSuggestions.tryGetValue(word, out word), return word |
|---|
| 1524 | else, return nil |
|---|
| 1525 | |
|---|
| 1526 | |
|---|
| 1527 | ## |
|---|
| 1528 | ## Services to this |
|---|
| 1529 | ## |
|---|
| 1530 | |
|---|
| 1531 | def _addMessage(message as SourceException) |
|---|
| 1532 | # Cobra can sometimes generate duplicate messages. Specifically, a constructed type might generate a message that its generic def did. |
|---|
| 1533 | # Also, assert statements whose conditions cause C# warnings will get duplicate warnings because of the expression breakdown. |
|---|
| 1534 | # Yeah, ideally, this would be fixed, but sometimes that's harder than just removing the duplicates: |
|---|
| 1535 | key = if(message.hasSourceSite, '[message.fileName]:[message.lineNum]', '') |
|---|
| 1536 | if _messagesPerSourceLine.containsKey(key) |
|---|
| 1537 | for se in _messagesPerSourceLine[key] |
|---|
| 1538 | if se.message.toLower == message.message.toLower |
|---|
| 1539 | if Utils.isDevMachine or .verbosity >= 3 |
|---|
| 1540 | print 'Skipping duplicate message:', message.consoleString |
|---|
| 1541 | return |
|---|
| 1542 | else |
|---|
| 1543 | _messagesPerSourceLine[key] = List<of SourceException>() |
|---|
| 1544 | _messagesPerSourceLine[key].add(message) |
|---|
| 1545 | _messages.add(message) |
|---|
| 1546 | if message.isError |
|---|
| 1547 | _errors.add(message) |
|---|
| 1548 | else |
|---|
| 1549 | _warnings.add(message) |
|---|
| 1550 | |
|---|
| 1551 | def initBackEnd |
|---|
| 1552 | require .backEnd is nil |
|---|
| 1553 | ensure .backEnd |
|---|
| 1554 | branch .options.get('back-end') |
|---|
| 1555 | on 'none', _backEnd = ClrBackEnd(this) # TODO-SELFHOST |
|---|
| 1556 | on 'clr', _backEnd = ClrBackEnd(this) |
|---|
| 1557 | on 'jvm' |
|---|
| 1558 | _backEnd = JvmBackEnd(this) |
|---|
| 1559 | if .options['number'] == 'decimal', .options['number'] = 'float' |
|---|
| 1560 | on 'objc', _backEnd = ObjcBackEnd(this) |
|---|
| 1561 | else, throw FallThroughException(.options.get('back-end')) |
|---|
| 1562 | |
|---|
| 1563 | |
|---|
| 1564 | class InternalError |
|---|
| 1565 | inherits SourceException |
|---|
| 1566 | """ |
|---|
| 1567 | Represents cases where the Cobra compiler itself has experienced an internal exception. |
|---|
| 1568 | """ |
|---|
| 1569 | |
|---|
| 1570 | var _fileName as String? |
|---|
| 1571 | var _lineNum as int? |
|---|
| 1572 | var _internalException as Exception |
|---|
| 1573 | |
|---|
| 1574 | cue init(fileName as String, lineNum as int, message as String, internalException as Exception) |
|---|
| 1575 | .init(message, internalException) |
|---|
| 1576 | _fileName = fileName |
|---|
| 1577 | _lineNum = lineNum |
|---|
| 1578 | |
|---|
| 1579 | cue init(message as String, internalException as Exception) |
|---|
| 1580 | base.init(('COBRA INTERNAL ERROR / ' + internalException.getType.name + ' / ' + message.replace('\r','').replace('\n','; ').trim)[:1024]) |
|---|
| 1581 | _internalException = internalException |
|---|
| 1582 | |
|---|
| 1583 | get isError as bool is override |
|---|
| 1584 | return true |
|---|
| 1585 | |
|---|
| 1586 | get hasSourceSite as bool is override |
|---|
| 1587 | return _fileName is not nil |
|---|
| 1588 | |
|---|
| 1589 | get fileName as String is override |
|---|
| 1590 | return _fileName to ! |
|---|
| 1591 | |
|---|
| 1592 | get lineNum as int is override |
|---|
| 1593 | return _lineNum to ! |
|---|
| 1594 | |
|---|
| 1595 | |
|---|
| 1596 | class Replacement |
|---|
| 1597 | |
|---|
| 1598 | cue init(colNum as int, oldText as String, newText as String) |
|---|
| 1599 | base.init |
|---|
| 1600 | _colNum = colNum |
|---|
| 1601 | _oldText = oldText |
|---|
| 1602 | _newText = newText |
|---|
| 1603 | |
|---|
| 1604 | get colNum from var as int |
|---|
| 1605 | |
|---|
| 1606 | get oldText from var as String |
|---|
| 1607 | |
|---|
| 1608 | get newText from var as String |
|---|
| 1609 | |
|---|
| 1610 | |
|---|
| 1611 | class TestCompiler inherits Compiler |
|---|
| 1612 | """ |
|---|
| 1613 | Used by SharpCompilationMessage.test |
|---|
| 1614 | CC: should be inside the test or easy to remove via a test attribute |
|---|
| 1615 | TODO: should inherit an AbstractCompiler |
|---|
| 1616 | """ |
|---|
| 1617 | |
|---|
| 1618 | cue init |
|---|
| 1619 | base.init(0) |
|---|
| 1620 | _backEnd = BasicBackEnd(this) |
|---|
| 1621 | |
|---|
| 1622 | pro verbosity as int is override |
|---|
| 1623 | get |
|---|
| 1624 | return 0 |
|---|
| 1625 | set |
|---|
| 1626 | pass |
|---|
| 1627 | |
|---|
| 1628 | def cobraLineNumForCurly(fileName as String, lineNum as int) as int is override |
|---|
| 1629 | return lineNum |
|---|
| 1630 | |
|---|
| 1631 | |
|---|