Summary
Cobra 0.6 adds a new Exception Report for faster postmortem diagnosis, adds yield statements, improves error checking, makes other improvements to the language and runtime, and fixes over 50 bugs and more.
Exception Report
When a Cobra program throws an uncaught exception, an informative report will be generated that includes:
- Environment details (program name, date and time, command line args, etc.)
- Full exception details (see below)
- Object tables (see below)
- Stack frame details (if "cobra -detailed-stack-trace" or "cobra -dst" was turned on)
The report is HTML formatted for easy reading and object navigation. Its purpose is to help you diagnose your program's bug by providing easy-to-read details on the state of the program at the point of the exception.
The exception details include:
- The location of the exception (filename and line number)
- The .toString and .message of the exception
- All properties of the exception
- In the case of assert/require/ensure, a level-by-level evaluation of the expression that failed (called the "expression breakdown")
- The value of this
- An "object table" for this and other expression tables
The value of this and any objects encountered in the expression breakdown are displayed as HTML links which jump to an "object table" further down in the report. Each object table displays the class name of the object, its .toString value and all its property values in alphabetical order. The object table may itself have links to other objects. After navigating through one or more object references, you can use your browser's back button to revisit previous locations in the report.
Detailed Stack Trace
The exception report can provide details of each stack frame including the usual items (class name, method name, filename, line number) as well as the original values of arguments and the last values of all arguments and locals. As you might expect, any objects are displayed as links which when clicked lead to the table detailing that object.
Capturing these values can be extremely useful for troubleshooting, but also expensive. A program may slow down by 4 X when doing so. For that reason, the detailed stack trace is turned off by default. You can turn it on via a command line option:
cobra -dst MyProgram.cobra # or the full option name: cobra -detailed-stack-frame MyProgram.cobra
Clicking Back to Source Code
The assert statement will produce a clickable link for the source location (filename and line number) if the environment variable COBRA_EDIT_LINK is set. Likewise for ensure and require conditions that fail. Below are two examples in Bourne shell. The first invokes a local web server containing a CGI script that will invoke the local editor to open the file at the given line number. The second performs the same task but more directly due to the TextMate editor's direct support of such URLs. The use of "FILE" and "LINE" is literal and case sensitive: The Cobra run-time will substitute the correct values when generating the exception report.
export COBRA_EDIT_LINK='http://localhost:8888/cgi-bin/edit.cgi?file=FILE&line=LINE' export COBRA_EDIT_LINK='txmt://open/?url=file://FILE&line=LINE'
Example
Here is an example of an exception report (5MB) with a detailed stack trace. Note that reports for your own programs will be easier to understand as the classes, methods and objects will be familiar to you.
Additional Details of the Exception Report
- To prevent the report from consuming hundreds of megabytes, or even gigabytes, there is a cut off point of 250 objects total included in the report. These are the first objects in the report, such as the exception and the stack frames, and then the objects related to those and the objects related to those and so on. In practice, 250 is usually enough to diagnose a problem, but if you need more (or less) you can control this with:
- The report is fairly immune to further exceptions thrown by .toString and various properties of objects that are included in the report.
- In previous versions of Cobra, the 'detailed stack trace' was called the 'super stack trace' and was output as plain text instead of formatted HTML. Also, the objects were not output in detail (no object tables). Also, there is less runtime overhead than in previous versions of Cobra.
- Objects can provide custom entries in their table views found in the exception report. The most straightforward approach is to simply add another property which will then show up in the object table like any other property. But if you wish to populate the table imperatively, you can do so by implementing .extendObjectTable like so:
Yield Statements
Cobra now has yield and yield break statements which are equivalent to their C# versions. You can conveniently use these in a normal looking method or property as opposed to explicitly implementing an IEnumerator class that manages state.
Using these, you can have the implementation of IEnumerator or IEnumerator<of T> generated for you.
For an example, see How to Iterate Through Recursive Data With Yield.
Because Cobra is a CLI language and adopts some C# features, you can leverage existing articles. The following may be helpful when trying to understand the yield statement:
New potential errors relating to yield and return include:
- Use "yield" instead of "yield return".
- Cannot use yield statements in constructors.
- Cannot yield a value in the body of a try block with a catch clause.
- Cannot yield a value in the body of a catch clause.
- Cannot yield in the body of a finally clause.
- Cannot yield unless the return type is an iterator type (IEnumerable or IEnumerator, generic or not). Try "IEnumerable<of TYPE>".
- Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration.
- Cannot return TYPEA because "METHOD" is declared to return a TYPEB.
Other Language Improvements
- A shorter form of casting can be used when the purpose of the cast is to make the expression non-nil or nilable. This shorter form leaves out the type and uses either ! to indicate "non-nilable" or ? to indicate "nilable". For example,
The short forms are strongly preferred as the long forms may imply that the given expression being cast is not already the casted type (such as String or List<of Bar>). Consequently, there are warnings when using the long forms over the short forms such as:
The given expression is already a "String", but nilable. You can just use "to !". The given expression is already a "String", but not nilable. You can just use "to ?".
- Added ability to combine enums with the call syntax, for example, AnchorStyle(Left, Right).
- The type inferred for assigning nil to a new local variable is now dynamic? instead of the rather unusable internal "nil type" that was inferred before.
- There are some new warnings for extraneous syntactical elements such as using parentheses around the condition of an if statement or the expression of a return statement:
The idea is to encourage a "normal form" for Cobra syntax so that Cobra code you see in examples, blogs, wikis, books and various projects looks similar. The benefit is the same as that for the conventions of writing English: youspendlesstimedIsCeRnInGSYntax::and::immediately get into semantics.
- The if inherits statement now groks assignments in the condition so that more succinct code can be written like so:
Previously that had to be written as:
or:
or:
- Support generics with the same name, but overloaded by parameter count (like in C#):
- Support dotted namespace declarations like namespace Foo.Bar.
- Enhanced Cobra to invoke .compareTo—when present and appropriate—when using comparison operators such as x < y.
- Regarding truth values, System.Object is now treated like other classes and is considered true if non-nil and false if nil (which can only happen for type Object?). Only dynamic and dynamic? get special run-time interpretation that checks for bools, ints, etc.
- Reserved some additional keywords for future use: end extend global inlined number objc
Library and Runtime Improvements
- When assert, require or ensure fail, they now automatically include the value of this—as rendered by CobraCore.toTechString. For shared methods, this will simply be the type since there is no instance. The value of this shows up in both a plain text dump of the assertion as well as the new Cobra Exception Report (including a link to the object table for further details including all property values).
- Enhanced CobraCore.toTechString to suffix the name of the object's type if that name is not contained in the toString result of the object. And to return "EnumType.EnumName enum" instead of "EnumName". These improvements show up both when you invoke .toTechString yourself as well as in the Cobra Exception Report which uses it.
- Added new Set.cobra in Reusables directory with a generic collection class representing simple sets.
- Enhance CobraCore.toTechString to display the contents of objects that are IEnumerable.
- Added CobraCore.isRunningOnMono.
- Added new .willOutputDirectoryNames property to Tracer which defaults to false and makes trace output a tad easier to read.
- Cobra now detects runaway/infinite recursion when -detailed-stack-trace (or -dst) is turned on. The resulting error message is often more informative than default behavior provided by either Microsoft .NET or Novell Mono for this situation. You can control the maximum number of stack frames with CobraCore.maxStackFrames which defaults to 500. Upon exceeding the maximum number of stack frames, Cobra will print a portion of them to help you diagnose the runaway recursion. The number of frames printed is controlled by CobraCore.numLastMaxStackFrames.
Command Line Improvements
- Created a posix-install script that installs Cobra on UNIX-like systems such as Mac OS X, Linux and BSD.
- Cobra now looks for a COBRA_EDITOR environment variable with values such as "uedit32_FILE/LINE" or "mate FILE -l LINE". The editor will be invoked if an error occurs. Note that underscores are just a convenience for spaces. You could also specify this information with the -editor command line option.
- The Cobra command line program now prints messages at the end of compilation such as:
Compilation failed - 8 errors, 1 warning Compilation succeeded - 1 warning Compilation succeeded
No message is printed if Cobra is going to run the program and there are no errors or warnings.
- Added "cobra -c" as a short form of "cobra -compile".
- "cobra -help" now wraps to the width of the console.
- The first verbose level for cobra (either "cobra -v=1 ..." or simply "cobra -v ...") now prints more useful information.
- Added now option "cobra -include-tests=0 ..." so that unit test code can be turned-off/excluded. The default is to include them.
- Added new option "cobra -keep-intermediate-files ..." (or "cobra -kif ...") because Cobra now deletes these by default.
- Added a -color option as in "cobra -c -color Foo.cobra" which, as "cobra -help" will tell you, colorizes the output of error messages and the messages "Compilation failed" and "Compilation succeeded" (as red, red and blue).
- For convenience, the command line option "-target=lib" now implies "-compile".
- Added "-output-html" command line option primarily for TextMate editor integration.
- Set the command line's exit code to 1 if there were compilation errors.
How To's
There are three new How To's:
- Use Nil and Nilable Types
- Use Dynamic Typing
- Access MySQL. This could easily be adapted to other databases.
Improved Error Checking
- Improved error message from:
Cannot find a definition for "nwLine" in "." whose type is "System.IO.TextWriter".
to:
Cannot find a definition for "nwLine" in "Console.out" whose type is "System.IO.TextWriter".
- Cobra no longer allows comparisons between types that can never be meaningfully compared (such as a Customer and an integer).
- Cobra now gives a sensible error message when a constructor contains the invocation of another constructor past its first statement.
- Better error message for unknown members (such as misspelled method names).
- Give a good error message when a for statement or other statement uses a pre-existing local variable whose type is not compatible with the statement.
- Fixed: A statement composed of a single variable reference (ex: "x") causes an internal compiler error instead of a regular error message.
- Fixed: Warnings are sometimes undercounted ("Compilation succeeded - 5 warnings" when there are actually 9).
- Fixed: The error message to use implements instead of inherits displays debugging information, making the message nearly unreadable.
- Fixed: The warning "Setting a parameter to a class variable is often a mistake." is not suppressed in cases where it doesn't apply. (That would be when the statement is contained in an if statement that references the parameter such as if param is nil.)
Bug Fixes
Language Bug Fixes
- Fixed: Using a type, such as SomeClass, on the right hand side of a comparison causes a false error.
- Fixed: Using list literals in an assert (or require or ensure) expression can cause an internal compiler error.
- Fixed: Error recovery in a binary math expression (addition, subtraction, etc.) causes an internal compiler error.
- Fixed: The expression obj implements ISomeInterface causes an internal error.
- Fixed: System.Object methods (getType, getHashCode, toString, etc.) on dynamically typed receivers does not work.
- Fixed: A dynamic typed expression cannot be used as the starting or stopping value of a numeric for loop.
- Fixed: Invoking methods on dynamic values without parens causes a runtime exception.
- Fixed: Dynamically typed values cannot be used as indexes.
- Fixed: Using arithmetic operators on invalid types causes an internal compiler error.
- Fixed: Invoking base.foo in a method does not always imply is override or is new as it should.
- Fixed: List<of Foo> cannot be returned from a method or property returning IList<of Foo>.
- Fixed: Defining .memberwiseClone in a class generates a false compile-time error message.
- Fixed: Declaring two members with the same name but different case (for example, "xxx" and "xXx") causes an internal error instead of a regular error message.
- Fixed: A commented out expression in require or ensure causes a false parse error.
- Fixed: structs in Cobra came out as classes in .NET.
- Fixed: Under certain circumstances, a symbol (such as "Path") cannot be found even though there is a use for its namespace (such as System.IO).
- Fixed: Various problems with qualified types including instantiating them and using them in if-inherits.
- Fixed: Using C# keywords as parameter names or local variable names causes problems. (C# is the current backend for Cobra.)
- Fixed: Errors that had no specific source file (such as "no suitable entry") cause an internal compiler error.
- Fixed: Branch statements followed by method declarations cause a false parse error.
- Fixed: Cobra allows instance methods to be invoked on classes if the class containing the code had a property with the same name as the class.
- Fixed: Cobra gives the wrong error message when a class implements an interface member of an interface implemented by the base class which was missing that member. How obscure is that?
- Fixed: Commented out enumeration values confuse the parser.
- Fixed: Declaring an object variable twice crashes instead of giving an error message.
- Fixed: Calling a local variable assigned to a type is not sufficient to suppress the warning that the variable is unused.
- Fixed: Looping through a list of enums causes a false compilation error.
- Fixed: Local variables cannot be named result without causing an internal error (the problem stems from contract support).
- Fixed: Instantiating a qualified array type causes a false compilation error.
- Fixed: Looping through a method or property that returns a qualified array type causes an internal compiler error.
- Fixed: A duplicate declaration in a namespace causes an internal error/exception instead of reporting a normal error message.
- Fixed: trace all fails in shared methods and properties.
- Fixed: Trailing whitespace after a doc string delimiter (""") confuses the parser.
Generic Bug Fixes
- Fixed: Accessing overloaded methods of a constructed type (one made from a generic) causes an internal error.
- Fixed: trace cannot be used in the shared member of a generic class (or struct).
- Fixed: Generic classes cannot have a test section.
- Fixed: Cobra can get confused about the parameter types and return types of methods in generic classes.
- Fixed: Type inference fails when using for loops with someDict.keys or someDict.values or someStack.
- Fixed: This code fails:
- Fixed: Inheriting generic classes fails.
- Fixed: Using IComparable<of T> is buggy.
- Fixed: Cannot instantiate a generic type argument.
Library Bug Fixes
- Fixed: SliceException message states 'start' where it should state 'stop'.
- Fixed: ICollection is "missing" the copyTo method interface.
Other Bug Fixes
- Fixed: Regarding the detailed stack trace (formely the "super stack trace"), exceptions caught at a higher level are erroneously preserved and reported as being the bad stack trace.
- Fixed: for loop variables and catch variables do not show up in the detailed stack trace area of the Cobra Exception Report.
- Fixed: "cobra -test foo" does not run unit tests on Mono systems (typically Linux and Mac).
- Fixed: The compiler sometimes hangs when generating numerous warnings or errors.
- Fixed: "cobra -v Foo.cobra" fails unless you add "=1" to the "-v".
- Fixed: Tokenizing errors such as "Cannot mix tabs and spaces error" are off by one line and do not pass the line number when using the "-editor" command line option.
- Fixed: The compiler cannot handle filenames with spaces on Novell Mono systems.
Tech Notes
This section contains fairly obscure technical notes that the average Cobra user won't need. These notes are for completeness and to aid implementors of the Cobra compiler.
- Added CobraCore.dumpStackAsHtml(tw as TextWriter) which is the backbone for the Cobra Exception Report. You don't typically have to invoke this yourself. The old CobraCore.dumpStack is now renamed to CobraCore.dumpStackAsText.
- Likewise there is a new CobraCore.handleUnhandledException, but again you're unlikely to invoked this directly since Cobra sets it up for you.
- There is a new ImplementationNotes.text file in Source directory with notes on the implementation.
Future Work
The following doesn't cover everything on the TO DO list, but some of the major areas left to tackle include:
- Open source Cobra under the MIT or BSD licenses.
- Support out and inout arguments.
- Delegates and events.
- Attributes.
- Class and interface extensions.
- Partial classes.
- Closures / anonymous methods.
- Code flow analysis.
- LINQ.
- DLR integration.