Wiki

root/cobra/trunk/Source/Cobra.Lang/CobraCore.cobra

Revision 2578, 16.5 KB (checked in by Charles.Esterbrook, 10 months ago)

JVM back-end progress, courtesy of hopscc.
credit:hopscc

  • Property svn:eol-style set to native
Line 
1use System.Reflection
2
3
4namespace Cobra.Lang
5
6    class CobraCore
7
8        shared
9       
10            ## Release info
11
12            get versionDescription as String
13                """
14                Returns a textual description of the version such as "X.Y.Z" or "svn-post-X.Y.Z".
15                """
16                return 'svn-post-[.version]'
17                # return .version.toString
18           
19            get isOfficialRelease
20                return false
21
22            get version as Version
23                return Version(0, 8, 0)
24
25            get releaseNum as int
26                return 25
27
28
29            ## String Makers
30           
31            pro printStringMaker as StringMaker
32                """
33                Used by `print` statements, string substitutions and .toPrintString methods.
34                """
35                get
36                    return CobraImp._printStringMaker
37                set
38                    CobraImp._printStringMaker = value
39           
40            pro techStringMaker as StringMaker
41                """
42                Used by `assert` failures, `trace` statements and .toTechString methods.
43                """
44                get
45                    return CobraImp._techStringMaker
46                set
47                    CobraImp._techStringMaker = value
48
49
50            ## Program stuff
51
52            get commandLineArgs as List<of String>
53                return List<of String>(Environment.getCommandLineArgs)
54
55            get exePath as String
56                """
57                Returns the full path of the currently executing exe including directory, file name and extension.
58                Use `Path.getDirectoryName()` on this to get the directory containing the current exe.
59                """
60                test
61                    assert '.exe' in CobraCore.exePath
62                body
63                    return Assembly.getEntryAssembly.location
64
65            def exit(exitCode as int)
66                """ Exits the process. """
67                Environment.exit(exitCode)
68           
69            get newLine as String
70                """ Returns the newline for the current environment/platform. """
71                return Environment.newLine
72
73            get printDestination as TextWriter
74                """
75                Returns the TextWriter that print statements currently print to.
76                This can change via the block version of the `print to` statement.
77                """
78                return CobraImp.printDestination
79
80            def printDebuggingTips
81                print 'An unhandled exception has occurred.'
82                print
83                print 'Cobra debugging tips:'
84                print '    To get file name and line number information for the stack frames, use:'
85                print '        cobra -debug foo.cobra'
86                if CobraCore.isRunningOnMono
87                    print '    If running the executable through "mono" then also use --debug (double dash):'
88                    print '        mono --debug foo.exe ...'
89                print '    To get a post-mortem, HTML-formatted report with more details about your objects:'
90                print '        cobra -debug -exception-report foo.cobra'
91                print '    For even more information, try:'
92                print '        cobra -debug -exception-report -detailed-stack-trace foo.cobra'
93                print '    Or use the abbreviations:'
94                print '        cobra -d -er -dst foo.cobra'
95                # TODO: print '    See also: http://cobra-language.com/docs/debugging'
96                print
97           
98
99            ## Reset
100           
101            def reset(printDestination as TextWriter)
102                """
103                Resets the Cobra run-time support using the argument as the initial print destination on the "print to" stack.
104                This is occasionally needed in special circumstances such as running with Pex (http://research.microsoft.com/pex/).
105                """
106                CobraImp.reset(printDestination)
107
108            def reset
109                """
110                Resets the Cobra run-time support using Console.out.
111                """
112                .reset(Console.out)
113           
114
115            ## Tracer
116
117            var _tracer as Tracer?
118
119            pro tracer as Tracer
120                """
121                The global Tracer object used by `trace` statements.
122                Via this property, you can set this to your own instance,
123                or access the tracer to read or write its properties.
124                To turn off tracing, for example, you can write `CobraCore.tracer.isActive = false`.
125                """
126                get
127                    if _tracer is nil
128                        _tracer = Tracer()
129                    return _tracer to !
130                set
131                    _tracer = Tracer()
132
133
134            ## Run Tests
135
136            def runAllTests
137                """
138                Run all Cobra `test` sections in all assemblies using reflection to locate them.
139                """
140                if CobraImp.showTestProgress, listener = Cobra.Lang.Test.TextWriterListener(Console.out)
141                else, listener = Cobra.Lang.Test.TextWriterOnlyOnFailureListener(Console.out)
142                tr = Cobra.Lang.Test.TestRunner(listener)
143                tr.runAllTests
144                if listener.testFailures, CobraCore.exit(1)
145
146
147            ## Detailed stack trace
148
149            pro maxStackFrames as int
150                """
151                When detailed stack trace is on, this value limits the maximum stack depth before
152                Cobra will exit with a stack overflow error including a listing of the most
153                recent stack frames. The default value is 250. You can set to 0 to disable
154                stack overflow detection.
155                """
156                get
157                    return CobraImp._maxStackFrames
158                set
159                    require value == 0 or value > 9
160                    CobraImp._maxStackFrames = value
161
162            pro numLastMaxStackFrames as int
163                """
164                When stack overflow is detected, this value gives the number of most recent stack frames that will be printed.
165                """
166                get
167                    return CobraImp._numLastMaxStackFrames
168                set
169                    require value >= 2
170                    CobraImp._numLastMaxStackFrames = value
171                   
172            var _maxDumpObjectCount = 250
173   
174            pro maxDumpObjectCount from var
175                """
176                Controls the maximum number of objects dumped  in the exception report.
177                Defaults to 250 which can easily result in a 5MB exception report.
178                """
179
180            get hasDetailedStackTrace
181                return CobraImp.hasDetailedStackTrace
182
183            def handleUnhandledException(ex as Exception)
184                print
185                try
186                    print 'Unhandled Exception: [ex]'
187                catch exc as Exception
188                    print 'Unhandled Exception: CAUGHT EXCEPTION FOR [ex.getType.name].toString: [exc]'
189                fileName = 'cobra-exception-report.html'
190                print 'Writing exception report to [fileName]...'
191                using tw = File.createText(fileName)
192                    HtmlExceptionReportWriter().writeReport(tw, ex)
193                print 'Wrote [fileName]'
194                didOpen = false
195                for envVarName in ['COBRA_OPEN_HTML_ER_COMMAND', 'COBRA_OPEN_HTML_SST_COMMAND']  # SST is old. "super stack trace"
196                    cmd = Environment.getEnvironmentVariable(envVarName)
197                    if cmd and cmd.trim.length
198                        p = System.Diagnostics.Process()
199                        p.startInfo.fileName = cmd
200                        p.startInfo.arguments = fileName
201                        p.startInfo.useShellExecute = false
202                        p.start
203                        print '[cmd] [fileName]'
204                        didOpen = true
205                        continue
206                if not didOpen
207                    print 'You can set the environment variable COBRA_OPEN_HTML_ER_COMMAND'
208                    print 'to automatically open the exception report.'
209
210   
211            ## Type names and tech strings
212
213            def typeName(t as Type) as String
214                return CobraImp.typeName(t)
215
216            def toTechString(x as Object?) as String
217                return CobraImp.toTechString(x)
218
219
220            ## Will check control flags
221
222            # CC: make a public section/block here
223            var _willCheckInvariant = true
224                is public
225            var _willCheckRequire = true
226                is public
227            var _willCheckEnsure = true
228                is public
229            var _willCheckAssert = true
230                is public
231            var _willCheckNil = true
232                is public
233
234            pro willCheckInvariant from var
235            pro willCheckRequire from var
236            pro willCheckEnsure from var
237            pro willCheckAssert from var
238            pro willCheckNil from var
239
240            set willCheckAll as bool
241                .willCheckInvariant = value
242                .willCheckRequire = value
243                .willCheckEnsure = value
244                .willCheckAssert = value
245                .willCheckNil = value
246
247
248            ## Finding cobra command line program
249
250            def runCobraExe(args as String, process as out System.Diagnostics.Process?) as String
251                """
252                Runs cobra.exe with the given args, sets the process to the one created for cobra.exe and returns all output including stdio and stderr.
253                In addition to examining the output, you should be interested in process.exitCode which will be zero if there was no problems.
254                Uses CobraCore.findCobraExe and raises IOException if it cannot be found.
255                Uses CobraCore.runAndCaptureAllOutput(p) to run the process.
256                """
257                cobraExePath = CobraCore.findCobraExe
258                if cobraExePath is nil or cobraExePath == ''
259                    throw IOException('Cannot locate cobra.exe using CobraCore.findCobraExe. Consider invoking .findCobraExe with additional search paths and passing the result to .runCobraExe.')
260                return .runCobraExe(cobraExePath, args, out process)
261
262            def runCobraExe(cobraPath as String, args as String, process as out System.Diagnostics.Process?) as String
263                require cobraPath.length
264                p = System.Diagnostics.Process()
265                if cobraPath.toLower.endsWith('.exe') and CobraCore.isRunningOnMono
266                    p.startInfo.fileName = 'mono'
267                    p.startInfo.arguments = cobraPath + ' ' + args
268                else
269                    p.startInfo.fileName = cobraPath
270                    p.startInfo.arguments = args
271                # print '[p.startInfo.fileName] [p.startInfo.arguments]'
272                process = p
273                return CobraCore.runAndCaptureAllOutput(p)
274
275            def findCobraExe as String?
276                return .findCobraExe(nil)
277
278            def findCobraExe(extraPaths as IList<of String>?) as String?
279                """
280                Finds "cobra" script or "cobra.exe" on the current system by looking in various places:
281                    * the current directory
282                    * the directory where the current executable resides
283                    * the path of the COBRA environment variable
284                    * the system PATH
285                    * the path of the cobra.exe that produced the current executable
286                    * educated guesses like \WINDOWS\Program Files\Cobra\bin and /usr/local/bin
287                    * the extraPaths argument
288                """
289                # check previous cached result first
290                if _findCobraCache, return _findCobraCache
291                # check the current directory
292                path = _findCobraExe(Environment.currentDirectory)
293                if path, return path
294                # check the directory where the current executable resides
295                path = _findCobraExe(Path.getDirectoryName(CobraCore.exePath))
296                if path, return path
297                # check COBRA environment variable
298                path = Environment.getEnvironmentVariable('COBRA') ? ''
299                if path <> ''
300                    path = _findCobraExe(path)
301                    if path, return path
302                # check the PATH
303                paths = (Environment.getEnvironmentVariable('PATH') ? '').split(Path.pathSeparator)
304                for sysPath in paths
305                    path = _findCobraExe(sysPath)
306                    if path, return path
307                # check for the cobra that created the current executable
308                if File.exists(CompileTimeInfo.cobraPath), return CompileTimeInfo.cobraPath
309                # check common locations and extraPaths
310                morePaths = [
311                    r'\WINDOWS\Program Files\Cobra\bin',
312                    r'\Cobra\bin',
313                    r'/usr/local/bin',
314                    r'/usr/bin',
315                    r'/bin',
316                ]
317                if extraPaths, morePaths.addRange(extraPaths)
318                for path in morePaths
319                    path = _findCobraExe(path)
320                    if path, return path
321                # cannot find cobra.exe
322                return nil
323
324            def clearCobraExeCache
325                """
326                The `findCobraExe` method caches its results for subsequent speed.
327                This method clears the cache in the event you need to do so.
328                """
329                _findCobraCache = nil
330
331            var _findCobraCache as String?
332
333            def _findCobraExe(path as String) as String?
334                _findCobraCache = _findCobraExe(path, true)
335                return _findCobraCache
336
337            def _findCobraExe(path as String, firstTime as bool) as String?
338                path = _expandTilde(path)
339                if path <> CobraCore.exePath and path.endsWith('cobra.exe')
340                    # ^ first condition guards against test cases like "invoke-cobra.exe" from endlessly invoking themselves
341                    if File.exists(path), return path
342                    else, return nil
343                else if Directory.exists(path)
344                    exts = ['', '.cmd', '.bat', '.exe']
345                    for ext in exts
346                        filePath = Path.combine(path, 'cobra' + ext)
347                        if File.exists(filePath), return filePath
348                    for ext in exts
349                        filePath = Path.combine(Path.combine(path, 'bin'), 'cobra' + ext)
350                        if File.exists(filePath), return filePath
351                    if firstTime
352                        # check for a 'Cobra' subdirectory
353                        return _findCobraExe(Path.combine(path, 'Cobra'), false)
354                    else
355                        return nil
356                else
357                    return nil
358
359            def _expandTilde(path as String) as String
360                if path.startsWith('~/')
361                    home = Environment.getFolderPath(Environment.SpecialFolder.Personal)
362                    path = home + path[1:]
363                return path
364
365
366            ## Other util
367           
368            def hasContracts as bool
369                """
370                Returns true if the current process was compiled with -include-contracts:yes (the default).
371                This enables you to determine at run-time if contracts are present.
372                At least one use for this is:
373                    if CobraCore.hasContracts
374                        expect RequireException, .someMethod
375                    else
376                        expect SomeOtherException, .someMethod
377                """
378                try
379                    _hasContracts
380                catch RequireException
381                    return true
382                return false
383           
384            def _hasContracts
385                require false
386                pass
387
388            get runtimePlatform as String
389                return 'clr'
390                #return 'jvm'
391                # return 'objc'
392
393            var _isRunningOnMono as bool?
394           
395            get isRunningOnMono as bool
396                if _isRunningOnMono is nil
397                    _isRunningOnMono = sharp'System.Type.GetType("Mono.Runtime")' is not nil
398                return _isRunningOnMono to !
399
400            get isRunningOnUnix as bool
401                """
402                Returns true if the current process is running on Unix/Posix/Linux/BSD/etc.
403                """
404                platform = Environment.osVersion.platform to int
405                return platform in [4, 6, 128]  # http://www.mono-project.com/FAQ:_Technical
406
407            var _hasMonoVersionString as bool
408            var _monoVersionString as String?
409               
410            def monoVersionString as String?
411                """
412                Returns a string such as "2.0" or "1.2.6" or nil if a version number cannot be determined (usually indicating that Mono is not present).
413                """
414                if not _hasMonoVersionString
415                    _hasMonoVersionString = true
416                    mrt = sharp'System.Type.GetType("Mono.Runtime")' to Type?
417                    if mrt
418                        mi = mrt.getMethod('GetDisplayName', BindingFlags(Public, NonPublic, Static))
419                        if mi
420                            ret = mi.invoke(nil, nil)
421                            if ret inherits String
422                                s = ret.trim
423                                if s.startsWith('Novell'), s = s.replace('Novell', '').trim
424                                if s.startsWith('Mono'), s = s.replace('Mono', '').trim
425                                _monoVersionString = s
426                return _monoVersionString
427
428            var _random as Random?
429           
430            get random as Random
431                """ Returns a globally shared, random number generator (created on first request). """
432                if _random is nil, _random = Random()
433                return _random to !
434                       
435            def runAndCaptureAllOutput(process as System.Diagnostics.Process) as String
436                """
437                Example:
438                    p = System.Diagnostics.Process()
439                    p.startInfo.fileName = 'some-program'
440                    p.startInfo.arguments = 'foo bar baz'
441                    output = CobraCore.runAndCaptureAllOutput(p)
442                    trace p, p.exitCode, output
443                TODO: change to extension method on Process
444                """
445                return CobraImp.runAndCaptureAllOutput(process)
446
447            def noOp(args as vari dynamic?) as dynamic?
448                """
449                No operation. Primarily used in Cobra's own test suite to consume a local variable to avoid undesired warnings.
450                Takes any number of values and returns an undefined value of type `dynamic?` for maximum flexibility.
451                """
452                return 0
453
454            var _htmlEncodes = @[['&', '&amp;'], ['<', '&lt;'], ['>', '&gt;'], ['"', '&quot;']]
455
456            def htmlEncode(obj as Object?) as String
457                """
458                Return the HTML encoded version of the given object.
459                Returns the contents of the object if it is an instance of Html.
460                This is useful to display a plain ASCII text string on a web page.
461                """
462                if obj is nil, return ''
463                if obj inherits Html, return obj.contents
464                return .htmlEncode(obj.toString)
465           
466            def htmlEncode(s as String?) as String
467                """
468                Return the HTML encoded version of the given string.
469                This is useful to display a plain ASCII text string on a web page.
470                """
471                test
472                    assert .htmlEncode('foo') == 'foo'
473                    assert .htmlEncode('3 < 5') == '3 &lt; 5'
474                body
475                    if s is nil, return ''
476                    for code in _htmlEncodes
477                        s = s.replace(code[0], code[1])
478                    return s to !
479
480            def htmlDecode(s as String?) as String
481                """
482                Return the HTML decoded version of the given string.
483                """
484                test
485                    assert .htmlDecode('foo') == 'foo'
486                    assert .htmlDecode('3 &lt; 5') == '3 < 5'
487                body
488                    if s is nil, return ''
489                    for code in _htmlEncodes
490                        s = s.replace(code[1], code[0])
491                    return s to !
492
493            def urlEncode(s as String?) as String
494                """
495                Return a version of the string encode for and safe to use in URLs.
496                """
497                return UrlUtils.encode(s)
498
499            def urlDecode(s as String?) as String
500                """
501                Return the decoded version of a URL encoded string.
502                """
503                return UrlUtils.decode(s)
504                   
505            def singleCompare(left, op as String, right) as bool
506                test
507                    cases = [[0, "GT", -0.1, true], [0, "GE", -0.1, true],  [0, "LT", -0.1, false],  [0, "LE", -0.1, false]]
508                    for left, op, right, answer in cases
509                        assert .singleCompare(left, op, right) == answer
510                body
511                    branch op
512                        on 'EQ', return left == right
513                        on 'NE', return left <> right
514                        on 'GT', return left > right
515                        on 'LT', return left < right
516                        on 'GE', return left >= right
517                        on 'LE', return left <= right
518                        on 'IS', return left == right
519                        on 'ISNOT', return left <> right
520                        else, throw FallThroughException(op)
521
522            def chainedComparison(stuff as vari dynamic) as bool
523                things = List<of dynamic>(stuff)
524                for index in 0 : things.count - 1 : 2
525                    if not .singleCompare(things[index], things[index + 1], things[index + 2])
526                        return false
527                return true
Note: See TracBrowser for help on using the browser.