1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/analysis/stack.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,159 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +require({ after_gcc_pass: "cfg" }); 1.9 + 1.10 +include("gcc_util.js"); 1.11 +include("unstable/lazy_types.js"); 1.12 + 1.13 +function process_type(c) 1.14 +{ 1.15 + if ((c.kind == 'class' || c.kind == 'struct') && 1.16 + !c.isIncomplete) 1.17 + isStack(c); 1.18 +} 1.19 + 1.20 +/** 1.21 + * A BlameChain records a chain of one or more location/message pairs. It 1.22 + * can be used to issue a complex error message such as: 1.23 + * location: error: Allocated class Foo on the heap 1.24 + * locationofFoo: class Foo inherits from class Bar 1.25 + * locationofBar: in class Bar 1.26 + * locationofBarMem: Member Bar::mFoo 1.27 + * locationofBaz: class Baz is annotated NS_STACK 1.28 + */ 1.29 +function BlameChain(loc, message, prev) 1.30 +{ 1.31 + this.loc = loc; 1.32 + this.message = message; 1.33 + this.prev = prev; 1.34 +} 1.35 + 1.36 +BlameChain.prototype.toString = function() 1.37 +{ 1.38 + let loc = this.loc; 1.39 + if (loc === undefined) 1.40 + loc = "<unknown location>"; 1.41 + 1.42 + let str = '%s: %s'.format(loc.toString(), this.message); 1.43 + if (this.prev) 1.44 + str += "\n%s".format(this.prev); 1.45 + return str; 1.46 +}; 1.47 + 1.48 +function isStack(c) 1.49 +{ 1.50 + function calculate() 1.51 + { 1.52 + if (hasAttribute(c, 'NS_stack')) 1.53 + return new BlameChain(c.loc, '%s %s is annotated NS_STACK_CLASS'.format(c.kind, c.name)); 1.54 + 1.55 + for each (let base in c.bases) { 1.56 + let r = isStack(base.type); 1.57 + if (r != null) 1.58 + return new BlameChain(c.loc, '%s %s is a base of %s %s'.format(base.type.kind, base.type.name, c.kind, c.name), r); 1.59 + } 1.60 + 1.61 + for each (let member in c.members) { 1.62 + if (member.isFunction) 1.63 + continue; 1.64 + 1.65 + if (hasAttribute(member, 'NS_okonheap')) 1.66 + continue; 1.67 + 1.68 + let type = member.type; 1.69 + while (true) { 1.70 + if (type === undefined) 1.71 + break; 1.72 + 1.73 + if (type.isArray) { 1.74 + type = type.type; 1.75 + continue; 1.76 + } 1.77 + 1.78 + if (type.typedef) { 1.79 + if (hasAttribute(type, 'NS_stack')) 1.80 + return true; 1.81 + 1.82 + type = type.typedef; 1.83 + continue; 1.84 + } 1.85 + break; 1.86 + } 1.87 + 1.88 + if (type === undefined) { 1.89 + warning("incomplete type for member " + member + ".", member.loc); 1.90 + continue; 1.91 + } 1.92 + 1.93 + if (type.isPointer || type.isReference) 1.94 + continue; 1.95 + 1.96 + if (!type.kind || (type.kind != 'class' && type.kind != 'struct')) 1.97 + continue; 1.98 + 1.99 + let r = isStack(type); 1.100 + if (r != null) 1.101 + return new BlameChain(c.loc, 'In class %s'.format(c.name), 1.102 + new BlameChain(member.loc, 'Member %s'.format(member.name), r)); 1.103 + } 1.104 + return null; 1.105 + } 1.106 + 1.107 + if (!c.hasOwnProperty('isStack')) 1.108 + c.isStack = calculate(); 1.109 + 1.110 + return c.isStack; 1.111 +} 1.112 + 1.113 +function process_tree(fn) 1.114 +{ 1.115 + if (hasAttribute(dehydra_convert(fn), 'NS_suppress_stackcheck')) 1.116 + return; 1.117 + 1.118 + let cfg = function_decl_cfg(fn); 1.119 + 1.120 + for (let bb in cfg_bb_iterator(cfg)) { 1.121 + let it = bb_isn_iterator(bb); 1.122 + for (let isn in it) { 1.123 + if (isn.tree_code() != GIMPLE_CALL) 1.124 + continue; 1.125 + 1.126 + let name = gimple_call_function_name(isn); 1.127 + if (name != "operator new" && name != "operator new []") 1.128 + continue; 1.129 + 1.130 + // ignore placement new 1.131 + // TODO? ensure 2nd arg is local stack variable 1.132 + if (gimple_call_num_args(isn) == 2 && 1.133 + TREE_TYPE(gimple_call_arg(isn, 1)).tree_code() == POINTER_TYPE) 1.134 + continue; 1.135 + 1.136 + let newLhs = gimple_call_lhs(isn); 1.137 + if (!newLhs) 1.138 + error("Non assigning call to operator new", location_of(isn)); 1.139 + 1.140 + // if isn is the last of its block there are other problems... 1.141 + assign = it.next(); 1.142 + 1.143 + // Calls to |new| are always followed by an assignment, casting the void ptr to which 1.144 + // |new| was assigned, to a ptr variable of the same type as the allocated object. 1.145 + // Exception: explicit calls to |::operator new (size_t)|, which can be ignored. 1.146 + if (assign.tree_code() != GIMPLE_ASSIGN) 1.147 + continue; 1.148 + 1.149 + let assignRhs = gimple_op(assign, 1); 1.150 + if (newLhs != assignRhs) 1.151 + continue; 1.152 + 1.153 + let assignLhs = gimple_op(assign, 0); 1.154 + let type = TREE_TYPE(TREE_TYPE(assignLhs)); 1.155 + let dehydraType = dehydra_convert(type); 1.156 + 1.157 + let r = isStack(dehydraType); 1.158 + if (r) 1.159 + warning("constructed object of type '%s' not on the stack: %s".format(dehydraType.name, r), location_of(isn)); 1.160 + } 1.161 + } 1.162 +}