michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: require({ after_gcc_pass: "cfg" }); michael@0: michael@0: include("gcc_util.js"); michael@0: include("unstable/lazy_types.js"); michael@0: michael@0: function process_type(c) michael@0: { michael@0: if ((c.kind == 'class' || c.kind == 'struct') && michael@0: !c.isIncomplete) michael@0: isStack(c); michael@0: } michael@0: michael@0: /** michael@0: * A BlameChain records a chain of one or more location/message pairs. It michael@0: * can be used to issue a complex error message such as: michael@0: * location: error: Allocated class Foo on the heap michael@0: * locationofFoo: class Foo inherits from class Bar michael@0: * locationofBar: in class Bar michael@0: * locationofBarMem: Member Bar::mFoo michael@0: * locationofBaz: class Baz is annotated NS_STACK michael@0: */ michael@0: function BlameChain(loc, message, prev) michael@0: { michael@0: this.loc = loc; michael@0: this.message = message; michael@0: this.prev = prev; michael@0: } michael@0: michael@0: BlameChain.prototype.toString = function() michael@0: { michael@0: let loc = this.loc; michael@0: if (loc === undefined) michael@0: loc = ""; michael@0: michael@0: let str = '%s: %s'.format(loc.toString(), this.message); michael@0: if (this.prev) michael@0: str += "\n%s".format(this.prev); michael@0: return str; michael@0: }; michael@0: michael@0: function isStack(c) michael@0: { michael@0: function calculate() michael@0: { michael@0: if (hasAttribute(c, 'NS_stack')) michael@0: return new BlameChain(c.loc, '%s %s is annotated NS_STACK_CLASS'.format(c.kind, c.name)); michael@0: michael@0: for each (let base in c.bases) { michael@0: let r = isStack(base.type); michael@0: if (r != null) michael@0: 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); michael@0: } michael@0: michael@0: for each (let member in c.members) { michael@0: if (member.isFunction) michael@0: continue; michael@0: michael@0: if (hasAttribute(member, 'NS_okonheap')) michael@0: continue; michael@0: michael@0: let type = member.type; michael@0: while (true) { michael@0: if (type === undefined) michael@0: break; michael@0: michael@0: if (type.isArray) { michael@0: type = type.type; michael@0: continue; michael@0: } michael@0: michael@0: if (type.typedef) { michael@0: if (hasAttribute(type, 'NS_stack')) michael@0: return true; michael@0: michael@0: type = type.typedef; michael@0: continue; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (type === undefined) { michael@0: warning("incomplete type for member " + member + ".", member.loc); michael@0: continue; michael@0: } michael@0: michael@0: if (type.isPointer || type.isReference) michael@0: continue; michael@0: michael@0: if (!type.kind || (type.kind != 'class' && type.kind != 'struct')) michael@0: continue; michael@0: michael@0: let r = isStack(type); michael@0: if (r != null) michael@0: return new BlameChain(c.loc, 'In class %s'.format(c.name), michael@0: new BlameChain(member.loc, 'Member %s'.format(member.name), r)); michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: if (!c.hasOwnProperty('isStack')) michael@0: c.isStack = calculate(); michael@0: michael@0: return c.isStack; michael@0: } michael@0: michael@0: function process_tree(fn) michael@0: { michael@0: if (hasAttribute(dehydra_convert(fn), 'NS_suppress_stackcheck')) michael@0: return; michael@0: michael@0: let cfg = function_decl_cfg(fn); michael@0: michael@0: for (let bb in cfg_bb_iterator(cfg)) { michael@0: let it = bb_isn_iterator(bb); michael@0: for (let isn in it) { michael@0: if (isn.tree_code() != GIMPLE_CALL) michael@0: continue; michael@0: michael@0: let name = gimple_call_function_name(isn); michael@0: if (name != "operator new" && name != "operator new []") michael@0: continue; michael@0: michael@0: // ignore placement new michael@0: // TODO? ensure 2nd arg is local stack variable michael@0: if (gimple_call_num_args(isn) == 2 && michael@0: TREE_TYPE(gimple_call_arg(isn, 1)).tree_code() == POINTER_TYPE) michael@0: continue; michael@0: michael@0: let newLhs = gimple_call_lhs(isn); michael@0: if (!newLhs) michael@0: error("Non assigning call to operator new", location_of(isn)); michael@0: michael@0: // if isn is the last of its block there are other problems... michael@0: assign = it.next(); michael@0: michael@0: // Calls to |new| are always followed by an assignment, casting the void ptr to which michael@0: // |new| was assigned, to a ptr variable of the same type as the allocated object. michael@0: // Exception: explicit calls to |::operator new (size_t)|, which can be ignored. michael@0: if (assign.tree_code() != GIMPLE_ASSIGN) michael@0: continue; michael@0: michael@0: let assignRhs = gimple_op(assign, 1); michael@0: if (newLhs != assignRhs) michael@0: continue; michael@0: michael@0: let assignLhs = gimple_op(assign, 0); michael@0: let type = TREE_TYPE(TREE_TYPE(assignLhs)); michael@0: let dehydraType = dehydra_convert(type); michael@0: michael@0: let r = isStack(dehydraType); michael@0: if (r) michael@0: warning("constructed object of type '%s' not on the stack: %s".format(dehydraType.name, r), location_of(isn)); michael@0: } michael@0: } michael@0: }