2008-02-27 08:28:13 -08:00
/ * *
* A script for GCC - dehydra to analyze the Mozilla codebase and catch
* patterns that are incorrect , but which cannot be detected by a compiler . * /
2008-04-30 17:47:27 -07:00
/ * *
* Activate Treehydra outparams analysis if running in Treehydra .
* /
function treehydra _enabled ( ) {
return this . hasOwnProperty ( 'TREE_CODE' ) ;
}
if ( treehydra _enabled ( ) ) {
include ( 'outparams.js' ) ;
}
2008-02-27 08:28:13 -08:00
/ * *
* gClassMap maps class names to an object with the following properties :
*
* . final = true if the class has been annotated as final , and may not be
* subclassed
* . stack = true if the class has been annotated as a class which may only
* be instantiated on the stack
* /
var gClassMap = { } ;
function ClassType ( name )
{
this . name = name ;
}
ClassType . prototype = {
final : false ,
stack : false ,
} ;
2008-03-27 11:03:59 -07:00
function process _type ( c )
2008-02-27 08:28:13 -08:00
{
2008-03-27 11:03:59 -07:00
if ( c . kind == 'class' || c . kind == 'struct' )
get _class ( c , true ) ;
2008-02-27 08:28:13 -08:00
}
/ * *
* Get the ClassType for a type 'c'
*
* If allowIncomplete is true and the type is incomplete , this function
* will return null .
*
* If allowIncomplete is false and the type is incomplete , this function will
* throw .
* /
function get _class ( c , allowIncomplete )
{
var classattr , base , member , type , realtype , foundConstructor ;
var bases = [ ] ;
if ( c . isIncomplete ) {
if ( allowIncomplete )
return null ;
throw Error ( "Can't process incomplete type '" + c + "'." ) ;
}
if ( gClassMap . hasOwnProperty ( c . name ) ) {
return gClassMap [ c . name ] ;
}
for each ( base in c . bases ) {
realtype = get _class ( base , allowIncomplete ) ;
if ( realtype == null ) {
error ( "Complete type " + c + " has incomplete base " + base ) ;
return null ;
}
bases . push ( realtype ) ;
}
function hasAttribute ( attrname )
{
var attr ;
if ( c . attributes === undefined )
return false ;
for each ( attr in c . attributes ) {
if ( attr . name == 'user' && attr . value [ 0 ] == attrname ) {
return true ;
}
}
return false ;
}
classattr = new ClassType ( c . name ) ;
gClassMap [ c . name ] = classattr ;
// check for .final
if ( hasAttribute ( 'NS_final' ) ) {
classattr . final = true ;
}
// check for .stack
if ( hasAttribute ( 'NS_stack' ) ) {
classattr . stack = true ;
}
else {
for each ( base in bases ) {
if ( base . stack ) {
classattr . stack = true ;
break ;
}
}
}
if ( ! classattr . stack ) {
// Check members
for each ( member in c . members ) {
if ( member . isFunction )
continue ;
type = member . type ;
/* recurse through arrays and typedefs */
while ( true ) {
if ( type === undefined ) {
break ;
}
if ( type . isArray ) {
type = type . type ;
continue ;
}
if ( type . typedef ) {
type = type . typedef ;
continue ;
}
break ;
}
if ( type === undefined ) {
warning ( "incomplete type for member " + member + "." ) ;
continue ;
}
if ( type . isPointer || type . isReference ) {
continue ;
}
if ( ! type . kind || ( type . kind != 'class' && type . kind != 'struct' ) ) {
continue ;
}
var membertype = get _class ( type , false ) ;
if ( membertype . stack ) {
classattr . stack = true ;
break ;
}
}
}
// Check for errors at declaration-time
for each ( base in bases ) {
if ( base . final ) {
error ( "class '" + c . name + "' inherits from final class '" + base . name + "'." ) ;
}
}
// At the moment, any class that is .final has to have a constructor, or
// we can't detect callsites... this may change with treehydra.
if ( classattr . stack ) {
foundConstructor = false ;
for each ( member in c . members ) {
if ( member . isConstructor ) {
foundConstructor = true ;
break ;
}
}
if ( ! foundConstructor ) {
warning ( c . loc + ": class " + c . name + " is marked stack-only but doesn't have a constructor. Static checking can't detect instantiations of this class properly." ) ;
}
}
return classattr ;
}
/ * *
* Unwrap any array of types back to their base type .
* /
function unwrapArray ( t )
{
while ( t . isArray ) {
t = t . type ;
}
return t ;
}
function process _function ( f , stmts )
{
var stmt ;
function getLocation ( )
{
if ( stmt . loc )
return stmt . loc ;
return f . loc ;
}
function processVar ( v )
{
if ( v . isConstructor &&
v . fieldOf &&
get _class ( v . methodOf , false ) . stack &&
v . fieldOf . type . isPointer ) {
error ( getLocation ( ) + ": constructed object of type '" +
v . methodOf . name + "' not on the stack." ) ;
}
}
for each ( stmt in stmts ) {
iter ( processVar , stmt . statements ) ;
}
}