Bug 1031310 - remove xpcom/analysis/; r=bsmedberg

This commit is contained in:
Nathan Froyd 2014-06-27 11:36:55 -04:00
Родитель a328efc6da
Коммит 3a3b025f95
15 изменённых файлов: 0 добавлений и 2238 удалений

Просмотреть файл

@ -1,23 +0,0 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Upload a file attachment to MDC
Usage: python MDC-attach.py <file> <parent page name> <MIME type> <description>
Please set MDC_USER and MDC_PASSWORD in the environment
"""
import os, sys, deki
wikiuser = os.environ['MDC_USER']
wikipw = os.environ['MDC_PASSWORD']
file, pageid, mimetype, description = sys.argv[1:]
wiki = deki.Deki("http://developer.mozilla.org/@api/deki/", wikiuser, wikipw)
wiki.create_file(pageid, os.path.basename(file), open(file).read(), mimetype,
description)

Просмотреть файл

@ -1,22 +0,0 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Upload a page to MDC
Usage: python MDC-upload.py <file> <MDC-path>
Please set MDC_USER and MDC_PASSWORD in the environment
"""
import os, sys, deki
wikiuser = os.environ['MDC_USER']
wikipw = os.environ['MDC_PASSWORD']
(file, wikipath) = sys.argv[1:]
wiki = deki.Deki("http://developer.mozilla.org/@api/deki/", wikiuser, wikipw)
wiki.create_page(wikipath, open(file).read(), overwrite=True)

Просмотреть файл

@ -1,44 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include $(topsrcdir)/config/rules.mk
DUMP_CLASSES = \
nsAString_internal \
nsACString_internal \
$(NULL)
SPACE = $(NULL) $(NULL)
COMMA = ,
HGREV = $(shell hg -R $(topsrcdir) id -i)
classapi: DEHYDRA_MODULES = $(srcdir)/type-printer.js
classapi: TREEHYDRA_MODULES =
classapi: DEHYDRA_ARGS += --dump-types=$(subst $(SPACE),$(COMMA),$(strip $(DUMP_CLASSES))) --rev=$(HGREV)
classapi: $(call mkdir_deps,$(MDDEPDIR))
$(CCC) $(OUTOPTION)/dev/null -c $(COMPILE_CXXFLAGS) $(srcdir)/type-printer.cpp >classapi.out 2>&1
perl -e 'while (<>) {if (/DUMP-TYPE\((.*)\)/) {print "$$1 ";}}' <classapi.out >dumptypes.list
perl -e 'while (<>) {if (/GRAPH-TYPE\((.*)\)/) {print "$$1 ";}}' <classapi.out >graphtypes.list
$(EXIT_ON_ERROR) \
for class in `cat graphtypes.list`; do \
dot -Tpng -o$${class}-graph.png -Tcmapx -o$${class}-graph.map $${class}-graph.gv; \
done
$(EXIT_ON_ERROR) \
for class in `cat dumptypes.list`; do \
$(PYTHON) $(srcdir)/fix-srcrefs.py $(topsrcdir) < $${class}.html > $${class}-fixed.html; \
done
upload_classapi:
$(EXIT_ON_ERROR) \
for class in `cat dumptypes.list`; do \
$(PYTHON) $(srcdir)/MDC-upload.py $${class}-fixed.html en/$${class}; \
done
$(EXIT_ON_ERROR) \
for class in `cat graphtypes.list`; do \
$(PYTHON) $(srcdir)/MDC-attach.py $${class}-graph.png en/$${class} image/png 'Class inheritance graph'; \
done
GARBAGE += $(wildcard *.html) $(wildcard *.png) $(wildcard *.map) \
$(wildcard *.gv) classapi.out graphtypes.list dumptypes.list

Просмотреть файл

@ -1,346 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
""" deki.py - Access the wiki pages on a MindTouch Deki server via the API.
Here's what this code can do:
wiki = deki.Deki("http://developer.mozilla.org/@api/deki/", username, password)
page = wiki.get_page("Sheep")
print page.title
print page.doc.toxml()
page.title = "Bananas"
page.save()
There are also some additional methods:
wiki.create_page(path, content, title=, override=)
wiki.move_page(old, new)
wiki.get_subpages(page)
This module does not try to mimic the MindTouch "Plug" API. It's meant to be
higher-level than that.
"""
import sys
import urllib2, cookielib, httplib
import xml.dom.minidom as dom
from urllib import quote as _urllib_quote
from urllib import urlencode as _urlencode
import urlparse
from datetime import datetime
import re
__all__ = ['Deki']
# === Utils
def _check(fact):
if not fact:
raise AssertionError('check failed')
def _urlquote(s, *args):
return _urllib_quote(s.encode('utf-8'), *args)
def _make_url(*dirs, **params):
""" dirs must already be url-encoded, params must not """
url = '/'.join(dirs)
if params:
url += '?' + _urlencode(params)
return url
class PutRequest(urllib2.Request):
def get_method(self):
return "PUT"
# === Dream framework client code
# This handler causes python to "always be logged in" when it's talking to the
# server. If you're just accessing public pages, it generates more requests
# than are strictly needed, but this is the behavior you want for a bot.
#
# The users/authenticate request is sent twice: once without any basic auth and
# once with. Dumb. Feel free to fix.
#
class _LoginHandler(urllib2.HTTPCookieProcessor):
def __init__(self, server):
policy = cookielib.DefaultCookiePolicy(rfc2965=True)
cookiejar = cookielib.CookieJar(policy)
urllib2.HTTPCookieProcessor.__init__(self, cookiejar)
self.server = server
def http_request(self, req):
#print "DEBUG- Requesting " + req.get_full_url()
s = self.server
req = urllib2.HTTPCookieProcessor.http_request(self, req)
if ('Cookie' not in req.unredirected_hdrs
and req.get_full_url() != s.base + 'users/authenticate'):
s.login()
# Retry - should have a new cookie.
req = urllib2.HTTPCookieProcessor.http_request(self, req)
_check('Cookie' in req.unredirected_hdrs)
return req
class DreamClient:
def __init__(self, base, user, password):
"""
base - The base URI of the Deki API, with trailing slash.
Typically, 'http://wiki.example.org/@api/deki/'.
user, password - Your Deki login information.
"""
self.base = base
pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
pm.add_password(None, self.base, user, password)
ah = urllib2.HTTPBasicAuthHandler(pm)
lh = _LoginHandler(self)
self._opener = urllib2.build_opener(ah, lh)
def login(self):
response = self._opener.open(self.base + 'users/authenticate')
response.close()
def open(self, url):
return self._opener.open(self.base + url)
def _handleResponse(self, req):
"""Helper method shared between post() and put()"""
resp = self._opener.open(req)
try:
ct = resp.headers.get('Content-Type', '(none)')
if '/xml' in ct or '+xml' in ct:
return dom.parse(resp)
else:
#print "DEBUG- Content-Type:", ct
crud = resp.read()
#print 'DEBUG- crud:\n---\n%s\n---' % re.sub(r'(?m)^', ' ', crud)
return None
finally:
resp.close()
def post(self, url, data, type):
#print "DEBUG- posting to:", self.base + url
req = urllib2.Request(self.base + url, data, {'Content-Type': type})
return self._handleResponse(req)
def put(self, url, data, type):
#print "DEBUG- putting to:", self.base + url
req = PutRequest(self.base + url, data, {'Content-Type': type})
return self._handleResponse(req)
def get_xml(self, url):
resp = self.open(url)
try:
return dom.parse(resp)
finally:
resp.close()
# === DOM
def _text_of(node):
if node.nodeType == node.ELEMENT_NODE:
return u''.join(_text_of(n) for n in node.childNodes)
elif node.nodeType == node.TEXT_NODE:
return node.nodeValue
else:
return u''
def _the_element_by_name(doc, tagName):
elts = doc.getElementsByTagName(tagName)
if len(elts) != 1:
raise ValueError("Expected exactly one <%s> tag, got %d." % (tagName, len(elts)))
return elts[0]
def _first_element(node):
n = node.firstChild
while n is not None:
if n.nodeType == n.ELEMENT_NODE:
return n
n = node.nextSibling
return None
def _find_elements(node, path):
if u'/' in path:
[first, rest] = path.split(u'/', 1)
for child in _find_elements(node, first):
for desc in _find_elements(child, rest):
yield desc
else:
for n in node.childNodes:
if n.nodeType == node.ELEMENT_NODE and n.nodeName == path:
yield n
# === Deki
def _format_page_id(id):
if isinstance(id, int):
return str(id)
elif id is Deki.HOME:
return 'home'
elif isinstance(id, basestring):
# Double-encoded, per the Deki API reference.
return '=' + _urlquote(_urlquote(id, ''))
class Deki(DreamClient):
HOME = object()
def get_page(self, page_id):
""" Get the content of a page from the wiki.
The page_id argument must be one of:
an int - The page id (an arbitrary number assigned by Deki)
a str - The page name (not the title, the full path that shows up in the URL)
Deki.HOME - Refers to the main page of the wiki.
Returns a Page object.
"""
p = Page(self)
p._load(page_id)
return p
def create_page(self, path, content, title=None, overwrite=False):
""" Create a new wiki page.
Parameters:
path - str - The page id.
content - str - The XML content to put in the new page.
The document element must be a <body>.
title - str - The page title. Keyword argument only.
Defaults to the last path-segment of path.
overwrite - bool - Whether to overwrite an existing page. If false,
and the page already exists, the method will throw an error.
"""
if title is None:
title = path.split('/')[-1]
doc = dom.parseString(content)
_check(doc.documentElement.tagName == 'body')
p = Page(self)
p._create(path, title, doc, overwrite)
def attach_file(self, page, name, data, mimetype, description=None):
"""Create or update a file attachment.
Parameters:
page - str - the page ID this file is related to
name - str - the name of the file
data - str - the file data
mimetype - str - the MIME type of the file
description - str - a description of the file
"""
p = {}
if description is not None:
p['description'] = description
url = _make_url('pages', _format_page_id(page),
'files', _format_page_id(name), **p)
r = self.put(url, data, mimetype)
_check(r.documentElement.nodeName == u'file')
def get_subpages(self, page_id):
""" Return the ids of all subpages of the given page. """
doc = self.get_xml(_make_url("pages", _format_page_id(page_id),
"files,subpages"))
for elt in _find_elements(doc, u'page/subpages/page.subpage/path'):
yield _text_of(elt)
def move_page(self, page_id, new_title, redirects=True):
""" Move an existing page to a new location.
A page cannot be moved to a destination that already exists, is a
descendant, or has a protected title (ex. Special:xxx, User:,
Template:).
When a page is moved, subpages under the specified page are also moved.
For each moved page, the system automatically creates an alias page
that redirects from the old to the new destination.
"""
self.post(_make_url("pages", _format_page_id(page_id), "move",
to=new_title,
redirects=redirects and "1" or "0"),
"", "text/plain")
class Page:
""" A Deki wiki page.
To obtain a page, call wiki.get_page(id).
Attributes:
title : unicode - The page title.
doc : Document - The content of the page as a DOM Document.
The root element of this document is a <body>.
path : unicode - The path. Use this to detect redirects, as otherwise
page.save() will overwrite the redirect with a copy of the content!
deki : Deki - The Deki object from which the page was loaded.
page_id : str/id/Deki.HOME - The page id used to load the page.
load_time : datetime - The time the page was loaded,
according to the clock on the client machine.
Methods:
save() - Save the modified document back to the server.
Only the page.title and the contents of page.doc are saved.
"""
def __init__(self, deki):
self.deki = deki
def _create(self, path, title, doc, overwrite):
self.title = title
self.doc = doc
self.page_id = path
if overwrite:
self.load_time = datetime(2500, 1, 1)
else:
self.load_time = datetime(1900, 1, 1)
self.path = path
self.save()
def _load(self, page_id):
""" page_id - See comment near the definition of `HOME`. """
load_time = datetime.utcnow()
# Getting the title is a whole separate query!
url = 'pages/%s/info' % _format_page_id(page_id)
doc = self.deki.get_xml(url)
title = _text_of(_the_element_by_name(doc, 'title'))
path = _text_of(_the_element_by_name(doc, 'path'))
# If you prefer to sling regexes, you can request format=raw instead.
# The result is an XML document with one big fat text node in the body.
url = _make_url('pages', _format_page_id(page_id), 'contents',
format='xhtml', mode='edit')
doc = self.deki.get_xml(url)
content = doc.documentElement
_check(content.tagName == u'content')
body = _first_element(content)
_check(body is not None)
_check(body.tagName == u'body')
doc.removeChild(content)
doc.appendChild(body)
self.page_id = page_id
self.load_time = load_time
self.title = title
self.path = path
self.doc = doc
def save(self):
p = {'edittime': _urlquote(self.load_time.strftime('%Y%m%d%H%M%S')),
'abort': 'modified'}
if self.title is not None:
p['title'] = _urlquote(self.title)
url = _make_url('pages', _format_page_id(self.page_id), 'contents', **p)
body = self.doc.documentElement
bodyInnerXML = ''.join(n.toxml('utf-8') for n in body.childNodes)
reply = self.deki.post(url, bodyInnerXML, 'text/plain; charset=utf-8')
_check(reply.documentElement.nodeName == u'edit')
_check(reply.documentElement.getAttribute(u'status') == u'success')

Просмотреть файл

@ -1,41 +0,0 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Fix references to source files of the form [LOCpath]
so that they are relative to a given source directory.
Substitute the DOT-generated image map into the document.
"""
import os, sys, re
(srcdir, ) = sys.argv[1:]
srcdir = os.path.realpath(srcdir)
f = re.compile(r'\[LOC(.*?)\]')
def replacer(m):
file = m.group(1)
file = os.path.realpath(file)
if not file.startswith(srcdir):
raise Exception("File %s doesn't start with %s" % (file, srcdir))
file = file[len(srcdir) + 1:]
return file
s = re.compile(r'\[MAP(.*?)\]')
def mapreplace(m):
file = m.group(1)
c = open(file).read()
return c
for line in sys.stdin:
line = f.sub(replacer, line)
line = s.sub(mapreplace, line)
sys.stdout.write(line)

Просмотреть файл

@ -1,150 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
require({ version: '1.8' });
require({ after_gcc_pass: 'cfg' });
include('treehydra.js');
include('util.js');
include('gcc_util.js');
include('gcc_print.js');
include('unstable/adts.js');
include('unstable/analysis.js');
include('unstable/esp.js');
/* This implements the control flows-through analysis in bug 432917 */
var Zero_NonZero = {}
include('unstable/zero_nonzero.js', Zero_NonZero);
MapFactory.use_injective = true;
// Print a trace for each function analyzed
let TRACE_FUNCTIONS = 0;
// Trace operation of the ESP analysis, use 2 or 3 for more detail
let TRACE_ESP = 0;
// Print time-taken stats
let TRACE_PERF = 0;
function process_tree(fndecl) {
// At this point we have a function we want to analyze
if (TRACE_FUNCTIONS) {
print('* function ' + decl_name(fndecl));
print(' ' + loc_string(location_of(fndecl)));
}
if (TRACE_PERF) timer_start(fstring);
let cfg = function_decl_cfg(fndecl);
try {
let trace = TRACE_ESP;
let a = new FlowCheck(cfg, trace);
a.run();
} catch (e if e == "skip") {
return
}
print("checked " + decl_name(fndecl))
if (cfg.x_exit_block_ptr.stateIn.substates)
for each (let substate in cfg.x_exit_block_ptr.stateIn.substates.getValues()) {
for each (let v in substate.getVariables()) {
let var_state= substate.get (v)
let blame = substate.getBlame(v)
if (var_state != ESP.TOP && typeof var_state == 'string')
error(decl_name(fndecl) + ": Control did not flow through " +var_state, location_of(blame))
}
}
if (TRACE_PERF) timer_stop(fstring);
}
let track_return_loc = 0;
const FLOW_THROUGH = "MUST_FLOW_THROUGH"
function FlowCheck(cfg, trace) {
let found = create_decl_set(); // ones we already found
for (let bb in cfg_bb_iterator(cfg)) {
for (let isn in bb_isn_iterator(bb)) {
switch (isn.tree_code()) {
case GIMPLE_CALL: {
let fn = gimple_call_fndecl(isn)
if (!fn || decl_name(fn) != FLOW_THROUGH)
continue;
this.must_flow_fn = fn
break
}
case GIMPLE_RETURN:
let ret_expr = return_expr(isn);
if (track_return_loc && ret_expr) {
TREE_CHECK(ret_expr, VAR_DECL, RESULT_DECL);
this.rval = ret_expr;
}
break;
}
}
}
if (!this.must_flow_fn)
throw "skip"
let psvar_list = [new ESP.PropVarSpec(this.must_flow_fn, true)]
if (this.rval)
psvar_list.push(new ESP.PropVarSpec(this.rval))
this.zeroNonzero = new Zero_NonZero.Zero_NonZero()
ESP.Analysis.call(this, cfg, psvar_list, Zero_NonZero.meet, trace);
}
FlowCheck.prototype = new ESP.Analysis;
function char_star_arg2string(tree) {
return TREE_STRING_POINTER(tree.tree_check(ADDR_EXPR).operands()[0].tree_check(ARRAY_REF).operands()[0])
}
// State transition function. Mostly, we delegate everything to
// another function as either an assignment or a call.
FlowCheck.prototype.flowState = function(isn, state) {
switch (TREE_CODE(isn)) {
case GIMPLE_CALL: {
let fn = gimple_call_fndecl(isn)
if (fn == this.must_flow_fn)
state.assignValue(fn, char_star_arg2string(gimple_call_arg(isn, 0)), isn)
break
}
case GIMPLE_LABEL: {
let label = decl_name(gimple_op(isn, 0))
for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) {
if (label != value) continue
// reached the goto label we wanted =D
state.assignValue(this.must_flow_fn, ESP.TOP, isn)
}
break
}
case GIMPLE_RETURN:
for ([value, blame] in state.yieldPreconditions(this.must_flow_fn)) {
if (typeof value != 'string') continue
let loc;
if (this.rval)
for ([value, blame] in state.yieldPreconditions(this.rval)) {
loc = value
break
}
error("return without going through label "+ value, loc);
return
}
break
case GIMPLE_ASSIGN:
if (track_return_loc && gimple_op(isn, 0) == this.rval) {
state.assignValue(this.rval, location_of(isn), isn)
break
}
default:
this.zeroNonzero.flowState(isn, state)
}
}
// State transition function to apply branch filters. This is kind
// of boilerplate--we're just handling some stuff that GCC generates.
FlowCheck.prototype.flowStateCond = function(isn, truth, state) {
this.zeroNonzero.flowStateCond (isn, truth, state)
}

Просмотреть файл

@ -1,44 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* May-return analysis.
* This makes sense only for functions that return a value. The analysis
* determines the set of variables that may transitively reach the return
* statement. */
function MayReturnAnalysis() {
BackwardAnalysis.apply(this, arguments);
// May-return variables. We collect them all here.
this.vbls = create_decl_set();
// The return value variable itself
this.retvar = undefined;
}
MayReturnAnalysis.prototype = new BackwardAnalysis;
MayReturnAnalysis.prototype.flowState = function(isn, state) {
if (TREE_CODE(isn) == GIMPLE_RETURN) {
let v = return_expr(isn);
if (!v)
return;
if (v.tree_code() == RESULT_DECL) // only an issue with 4.3
throw new Error("Weird case hit");
this.vbls.add(v);
state.add(v);
this.retvar = v;
} else if (TREE_CODE(isn) == GIMPLE_ASSIGN) {
let lhs = gimple_op(isn, 0);
let rhs = gimple_op(isn, 1);
if (DECL_P(rhs) && DECL_P(lhs) && state.has(lhs)) {
this.vbls.add(rhs);
state.add(rhs);
}
for (let e in isn_defs(isn, 'strong')) {
if (DECL_P(e)) {
state.remove(e);
}
}
}
};

Просмотреть файл

@ -1,7 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEFINES['MOZILLA_INTERNAL_API'] = True

Просмотреть файл

@ -1,52 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Detect classes that should have overridden members of their parent
* classes but didn't.
*
* Example:
*
* struct S {
* virtual NS_MUST_OVERRIDE void f();
* virtual void g();
* };
*
* struct A : S { virtual void f(); }; // ok
* struct B : S { virtual NS_MUST_OVERRIDE void f(); }; // also ok
*
* struct C : S { virtual void g(); }; // ERROR: must override f()
* struct D : S { virtual void f(int); }; // ERROR: different overload
* struct E : A { }; // ok: A's definition of f() is good for subclasses
* struct F : B { }; // ERROR: B's definition of f() is still must-override
*
* We don't care if you define the method or not.
*/
function get_must_overrides(cls)
{
let mos = {};
for each (let base in cls.bases)
for each (let m in base.type.members)
if (hasAttribute(m, 'NS_must_override'))
mos[m.shortName] = m;
return mos;
}
function process_type(t)
{
if (t.isIncomplete || (t.kind != 'class' && t.kind != 'struct'))
return;
let mos = get_must_overrides(t);
for each (let m in t.members) {
let mos_m = mos[m.shortName]
if (mos_m && signaturesMatch(mos_m, m))
delete mos[m.shortName];
}
for each (let u in mos)
error(t.kind + " " + t.name + " must override " + u.name, t.loc);
}

Просмотреть файл

@ -1,880 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
require({ version: '1.8' });
require({ after_gcc_pass: 'cfg' });
include('treehydra.js');
include('util.js');
include('gcc_util.js');
include('gcc_print.js');
include('unstable/adts.js');
include('unstable/analysis.js');
include('unstable/esp.js');
let Zero_NonZero = {};
include('unstable/zero_nonzero.js', Zero_NonZero);
include('xpcom/analysis/mayreturn.js');
function safe_location_of(t) {
if (t === undefined)
return UNKNOWN_LOCATION;
return location_of(t);
}
MapFactory.use_injective = true;
// Print a trace for each function analyzed
let TRACE_FUNCTIONS = 0;
// Trace operation of the ESP analysis, use 2 or 3 for more detail
let TRACE_ESP = 0;
// Trace determination of function call parameter semantics, 2 for detail
let TRACE_CALL_SEM = 0;
// Print time-taken stats
let TRACE_PERF = 0;
// Log analysis results in a special format
let LOG_RESULTS = false;
const WARN_ON_SET_NULL = false;
const WARN_ON_SET_FAILURE = false;
// Filter functions to process per CLI
let func_filter;
if (this.arg == undefined || this.arg == '') {
func_filter = function(fd) true;
} else {
func_filter = function(fd) function_decl_name(fd) == this.arg;
}
function process_tree(func_decl) {
if (!func_filter(func_decl)) return;
// Determine outparams and return if function not relevant
if (DECL_CONSTRUCTOR_P(func_decl)) return;
let psem = OutparamCheck.prototype.func_param_semantics(func_decl);
if (!psem.some(function(x) x.check)) return;
let decl = rectify_function_decl(func_decl);
if (decl.resultType != 'nsresult' && decl.resultType != 'PRBool' &&
decl.resultType != 'void') {
warning("Cannot analyze outparam usage for function with return type '" +
decl.resultType + "'", location_of(func_decl));
return;
}
let params = [ v for (v in flatten_chain(DECL_ARGUMENTS(func_decl))) ];
let outparam_list = [];
let psem_list = [];
for (let i = 0; i < psem.length; ++i) {
if (psem[i].check) {
outparam_list.push(params[i]);
psem_list.push(psem[i]);
}
}
if (outparam_list.length == 0) return;
// At this point we have a function we want to analyze
let fstring = rfunc_string(decl);
if (TRACE_FUNCTIONS) {
print('* function ' + fstring);
print(' ' + loc_string(location_of(func_decl)));
}
if (TRACE_PERF) timer_start(fstring);
for (let i = 0; i < outparam_list.length; ++i) {
let p = outparam_list[i];
if (TRACE_FUNCTIONS) {
print(" outparam " + expr_display(p) + " " + DECL_UID(p) + ' ' +
psem_list[i].label);
}
}
let cfg = function_decl_cfg(func_decl);
let [retvar, retvars] = function() {
let trace = 0;
let a = new MayReturnAnalysis(cfg, trace);
a.run();
return [a.retvar, a.vbls];
}();
if (retvar == undefined && decl.resultType != 'void') throw new Error("assert");
{
let trace = TRACE_ESP;
for (let i = 0; i < outparam_list.length; ++i) {
let psem = [ psem_list[i] ];
let outparam = [ outparam_list[i] ];
let a = new OutparamCheck(cfg, psem, outparam, retvar, retvars, trace);
// This is annoying, but this field is only used for logging anyway.
a.fndecl = func_decl;
a.run();
a.check(decl.resultType == 'void', func_decl);
}
}
if (TRACE_PERF) timer_stop(fstring);
}
// Outparam check analysis
function OutparamCheck(cfg, psem_list, outparam_list, retvar, retvar_set,
trace) {
// We need to save the retvars so we can detect assignments through
// their addresses passed as arguments.
this.retvar_set = retvar_set;
this.retvar = retvar;
// We need both an ordered set and a lookup structure
this.outparam_list = outparam_list
this.outparams = create_decl_set(outparam_list);
this.psem_list = psem_list;
// Set up property state vars for ESP
let psvar_list = [];
for each (let v in outparam_list) {
psvar_list.push(new ESP.PropVarSpec(v, true, av.NOT_WRITTEN));
}
for (let v in retvar_set.items()) {
psvar_list.push(new ESP.PropVarSpec(v, v == this.retvar, ESP.TOP));
}
if (trace) {
print("PS vars");
for each (let v in this.psvar_list) {
print(" " + expr_display(v.vbl));
}
}
this.zeroNonzero = new Zero_NonZero.Zero_NonZero();
ESP.Analysis.call(this, cfg, psvar_list, av.meet, trace);
}
// Abstract values for outparam check
function AbstractValue(name, ch) {
this.name = name;
this.ch = ch;
}
AbstractValue.prototype.equals = function(v) {
return this === v;
}
AbstractValue.prototype.toString = function() {
return this.name + ' (' + this.ch + ')';
}
AbstractValue.prototype.toShortString = function() {
return this.ch;
}
let avspec = [
// Abstract values for outparam contents write status
[ 'NULL', 'x' ], // is a null pointer
[ 'NOT_WRITTEN', '-' ], // not written
[ 'WROTE_NULL', '/' ], // had NULL written to
[ 'WRITTEN', '+' ], // had anything written to
// MAYBE_WRITTEN is special. "Officially", it means the same thing as
// NOT_WRITTEN. What it really means is that an outparam was passed
// to another function as a possible outparam (outparam type, but not
// in last position), so if there is an error with it not being written,
// we can give a hint about the possible outparam in the warning.
[ 'MAYBE_WRITTEN', '?' ], // written if possible outparam is one
];
let av = {};
for each (let [name, ch] in avspec) {
av[name] = new AbstractValue(name, ch);
}
av.ZERO = Zero_NonZero.Lattice.ZERO;
av.NONZERO = Zero_NonZero.Lattice.NONZERO;
/*
av.ZERO.negation = av.NONZERO;
av.NONZERO.negation = av.ZERO;
// Abstract values for int constants. We use these to figure out feasible
// paths in the presence of GCC finally_tmp-controlled switches.
function makeIntAV(v) {
let key = 'int_' + v;
if (cachedAVs.hasOwnProperty(key)) return cachedAVs[key];
let s = "" + v;
let ans = cachedAVs[key] = new AbstractValue(s, s);
ans.int_val = v;
return ans;
}
*/
let cachedAVs = {};
// Abstract values for pointers that contain a copy of an outparam
// pointer. We use these to figure out writes to a casted copy of
// an outparam passed to another method.
function makeOutparamAV(v) {
let key = 'outparam_' + DECL_UID(v);
if (key in cachedAVs) return cachedAVs[key];
let ans = cachedAVs[key] =
new AbstractValue('OUTPARAM:' + expr_display(v), 'P');
ans.outparam = v;
return ans;
}
/** Return the integer value if this is an integer av, otherwise undefined. */
av.intVal = function(v) {
if (v.hasOwnProperty('int_val'))
return v.int_val;
return undefined;
}
/** Meet function for our abstract values. */
av.meet = function(v1, v2) {
// At this point we know v1 != v2.
let values = [v1,v2]
if (values.indexOf(av.LOCKED) != -1
|| values.indexOf(av.UNLOCKED) != -1)
return ESP.NOT_REACHED;
return Zero_NonZero.meet(v1, v2)
};
// Outparam check analysis
OutparamCheck.prototype = new ESP.Analysis;
OutparamCheck.prototype.split = function(vbl, v) {
// Can't happen for current version of ESP, but could change
if (v != ESP.TOP) throw new Error("not implemented");
return [ av.ZERO, av.NONZERO ];
}
OutparamCheck.prototype.updateEdgeState = function(e) {
e.state.keepOnly(e.dest.keepVars);
}
OutparamCheck.prototype.flowState = function(isn, state) {
switch (TREE_CODE(isn)) {
case GIMPLE_ASSIGN:
this.processAssign(isn, state);
break;
case GIMPLE_CALL:
this.processCall(isn, isn, state);
break;
case GIMPLE_SWITCH:
case GIMPLE_COND:
// This gets handled by flowStateCond instead, has no exec effect
break;
default:
this.zeroNonzero.flowState(isn, state);
}
}
OutparamCheck.prototype.flowStateCond = function(isn, truth, state) {
this.zeroNonzero.flowStateCond(isn, truth, state);
}
// For any outparams-specific semantics, we handle it here and then
// return. Otherwise we delegate to the zero-nonzero analysis.
OutparamCheck.prototype.processAssign = function(isn, state) {
let lhs = gimple_op(isn, 0);
let rhs = gimple_op(isn, 1);
if (DECL_P(lhs)) {
// Unwrap NOP_EXPR, which is semantically a copy.
if (TREE_CODE(rhs) == NOP_EXPR) {
rhs = rhs.operands()[0];
}
if (DECL_P(rhs) && this.outparams.has(rhs)) {
// Copying an outparam pointer. We have to remember this so that
// if it is assigned thru later, we pick up the write.
state.assignValue(lhs, makeOutparamAV(rhs), isn);
return;
}
// Cases of this switch that handle something should return from
// the function. Anything that does not return is picked up afteward.
switch (TREE_CODE(rhs)) {
case INTEGER_CST:
if (this.outparams.has(lhs)) {
warning("assigning to outparam pointer");
return;
}
break;
case EQ_EXPR: {
// We only care about testing outparams for NULL (and then not writing)
let [op1, op2] = rhs.operands();
if (DECL_P(op1) && this.outparams.has(op1) && expr_literal_int(op2) == 0) {
state.update(function(ss) {
let [s1, s2] = [ss, ss.copy()]; // s1 true, s2 false
s1.assignValue(lhs, av.NONZERO, isn);
s1.assignValue(op1, av.NULL, isn);
s2.assignValue(lhs, av.ZERO, isn);
return [s1, s2];
});
return;
}
}
break;
case CALL_EXPR:
/* Embedded CALL_EXPRs are a 4.3 issue */
this.processCall(rhs, isn, state, lhs);
return;
case INDIRECT_REF:
// If rhs is *outparam and pointer-typed, lhs is NULL iff rhs is
// WROTE_NULL. Required for testcase onull.cpp.
let v = rhs.operands()[0];
if (DECL_P(v) && this.outparams.has(v) &&
TREE_CODE(TREE_TYPE(v)) == POINTER_TYPE) {
state.update(function(ss) {
let val = ss.get(v) == av.WROTE_NULL ? av.ZERO : av.NONZERO;
ss.assignValue(lhs, val, isn);
return [ ss ];
});
return;
}
}
// Nothing special -- delegate
this.zeroNonzero.processAssign(isn, state);
return;
}
switch (TREE_CODE(lhs)) {
case INDIRECT_REF:
// Writing to an outparam. We want to try to figure out if we're
// writing NULL.
let e = TREE_OPERAND(lhs, 0);
if (this.outparams.has(e)) {
if (expr_literal_int(rhs) == 0) {
state.assignValue(e, av.WROTE_NULL, isn);
} else if (DECL_P(rhs)) {
state.update(function(ss) {
let [s1, s2] = [ss.copy(), ss]; // s1 NULL, s2 non-NULL
s1.assignValue(e, av.WROTE_NULL, isn);
s1.assignValue(rhs, av.ZERO, isn);
s2.assignValue(e, av.WRITTEN, isn);
s2.assignValue(rhs, av.NONZERO, isn);
return [s1,s2];
});
} else {
state.assignValue(e, av.WRITTEN, isn);
}
} else {
// unsound -- could be writing to anything through this ptr
}
break;
case COMPONENT_REF: // unsound
case ARRAY_REF: // unsound
case EXC_PTR_EXPR:
case FILTER_EXPR:
break;
default:
print(TREE_CODE(lhs));
throw new Error("ni");
}
}
// Handle an assignment x := test(foo) where test is a simple predicate
OutparamCheck.prototype.processTest = function(lhs, call, val, blame, state) {
let arg = gimple_call_arg(call, 0);
if (DECL_P(arg)) {
this.zeroNonzero.predicate(state, lhs, val, arg, blame);
} else {
state.assignValue(lhs, ESP.TOP, blame);
}
};
// The big one: outparam semantics of function calls.
OutparamCheck.prototype.processCall = function(call, blame, state, dest) {
if (!dest)
dest = gimple_call_lhs(call);
let args = gimple_call_args(call);
let callable = callable_arg_function_decl(gimple_call_fn(call));
let psem = this.func_param_semantics(callable);
let name = function_decl_name(callable);
if (name == 'NS_FAILED') {
this.processTest(dest, call, av.NONZERO, call, state);
return;
} else if (name == 'NS_SUCCEEDED') {
this.processTest(dest, call, av.ZERO, call, state);
return;
} else if (name == '__builtin_expect') {
// Same as an assign from arg 0 to lhs
state.assign(dest, args[0], call);
return;
}
if (TRACE_CALL_SEM) {
print("param semantics:" + psem);
}
if (args.length != psem.length) {
let ct = TREE_TYPE(callable);
if (TREE_CODE(ct) == POINTER_TYPE) ct = TREE_TYPE(ct);
if (args.length < psem.length || !stdarg_p(ct)) {
// TODO Can __builtin_memcpy write to an outparam? Probably not.
if (name != 'operator new' && name != 'operator delete' &&
name != 'operator new []' && name != 'operator delete []' &&
name.substr(0, 5) != '__cxa' &&
name.substr(0, 9) != '__builtin') {
throw Error("bad len for '" + name + "': " + args.length + ' args, ' +
psem.length + ' params');
}
}
}
// Collect variables that are possibly written to on callee success
let updates = [];
for (let i = 0; i < psem.length; ++i) {
let arg = args[i];
// The arg could be the address of a return-value variable.
// This means it's really the nsresult code for the call,
// so we treat it the same as the target of an rv assignment.
if (TREE_CODE(arg) == ADDR_EXPR) {
let v = arg.operands()[0];
if (DECL_P(v) && this.retvar_set.has(v)) {
dest = v;
}
}
// The arg could be a copy of an outparam. We'll unwrap to the
// outparam if it is. The following is cheating a bit because
// we munge states together, but it should be OK in practice.
arg = unwrap_outparam(arg, state);
let sem = psem[i];
if (sem == ps.CONST) continue;
// At this point, we know the call can write thru this param.
// Invalidate any vars whose addresses are passed here. This
// is distinct from the rv handling above.
if (TREE_CODE(arg) == ADDR_EXPR) {
let v = arg.operands()[0];
if (DECL_P(v)) {
state.remove(v);
}
}
if (!DECL_P(arg) || !this.outparams.has(arg)) continue;
// At this point, we may be writing to an outparam
updates.push([arg, sem]);
}
if (updates.length) {
if (dest != undefined && DECL_P(dest)) {
// Update & stored rv. Do updates predicated on success.
let [ succ_ret, fail_ret ] = ret_coding(callable);
state.update(function(ss) {
let [s1, s2] = [ss.copy(), ss]; // s1 success, s2 fail
for each (let [vbl, sem] in updates) {
s1.assignValue(vbl, sem.val, blame);
s1.assignValue(dest, succ_ret, blame);
}
s2.assignValue(dest, fail_ret, blame);
return [s1,s2];
});
} else {
// Discarded rv. Per spec in the bug, we assume that either success
// or failure is possible (if not, callee should return void).
// Exceptions: Methods that return void and string mutators are
// considered no-fail.
state.update(function(ss) {
for each (let [vbl, sem] in updates) {
if (sem == ps.OUTNOFAIL || sem == ps.OUTNOFAILNOCHECK) {
ss.assignValue(vbl, av.WRITTEN, blame);
return [ss];
} else {
let [s1, s2] = [ss.copy(), ss]; // s1 success, s2 fail
for each (let [vbl, sem] in updates) {
s1.assignValue(vbl, sem.val, blame);
}
return [s1,s2];
}
}
});
}
} else {
// no updates, just kill any destination for the rv
if (dest != undefined && DECL_P(dest)) {
state.remove(dest, blame);
}
}
};
/** Return the return value coding of the given function. This is a pair
* [ succ, fail ] giving the abstract values of the return value under
* success and failure conditions. */
function ret_coding(callable) {
let type = TREE_TYPE(callable);
if (TREE_CODE(type) == POINTER_TYPE) type = TREE_TYPE(type);
let rtname = TYPE_NAME(TREE_TYPE(type));
if (rtname && IDENTIFIER_POINTER(DECL_NAME(rtname)) == 'PRBool') {
return [ av.NONZERO, av.ZERO ];
} else {
return [ av.ZERO, av.NONZERO ];
}
}
function unwrap_outparam(arg, state) {
if (!DECL_P(arg) || state.factory.outparams.has(arg)) return arg;
let outparam;
for (let ss in state.substates.getValues()) {
let val = ss.get(arg);
if (val != undefined && val.hasOwnProperty('outparam')) {
outparam = val.outparam;
}
}
if (outparam) return outparam;
return arg;
}
// Check for errors. Must .run() analysis before calling this.
OutparamCheck.prototype.check = function(isvoid, fndecl) {
let state = this.cfg.x_exit_block_ptr.stateOut;
for (let substate in state.substates.getValues()) {
this.checkSubstate(isvoid, fndecl, substate);
}
}
OutparamCheck.prototype.checkSubstate = function(isvoid, fndecl, ss) {
if (isvoid) {
this.checkSubstateSuccess(ss);
} else {
let [succ, fail] = ret_coding(fndecl);
let rv = ss.get(this.retvar);
// We want to check if the abstract value of the rv is entirely
// contained in the success or failure condition.
if (av.meet(rv, succ) == rv) {
this.checkSubstateSuccess(ss);
} else if (av.meet(rv, fail) == rv) {
this.checkSubstateFailure(ss);
} else {
// This condition indicates a bug in outparams.js. We'll just
// warn so we don't break static analysis builds.
warning("Outparams checker cannot determine rv success/failure",
location_of(fndecl));
this.checkSubstateSuccess(ss);
this.checkSubstateFailure(ss);
}
}
}
/* @return The return statement in the function
* that writes the return value in the given substate.
* If the function returns void, then the substate doesn't
* matter and we just look for the return. */
OutparamCheck.prototype.findReturnStmt = function(ss) {
if (this.retvar != undefined)
return ss.getBlame(this.retvar);
if (this.cfg._cached_return)
return this.cfg._cached_return;
for (let bb in cfg_bb_iterator(this.cfg)) {
for (let isn in bb_isn_iterator(bb)) {
if (isn.tree_code() == GIMPLE_RETURN) {
return this.cfg._cached_return = isn;
}
}
}
return undefined;
}
OutparamCheck.prototype.checkSubstateSuccess = function(ss) {
for (let i = 0; i < this.psem_list.length; ++i) {
let [v, psem] = [ this.outparam_list[i], this.psem_list[i] ];
if (psem == ps.INOUT) continue;
let val = ss.get(v);
if (val == av.NOT_WRITTEN) {
this.logResult('succ', 'not_written', 'error');
this.warn([this.findReturnStmt(ss), "outparam '" + expr_display(v) + "' not written on NS_SUCCEEDED(return value)"],
[v, "outparam declared here"]);
} else if (val == av.MAYBE_WRITTEN) {
this.logResult('succ', 'maybe_written', 'error');
let blameStmt = ss.getBlame(v);
let callMsg;
let callName = "";
try {
let call = TREE_CHECK(blameStmt, GIMPLE_CALL, GIMPLE_MODIFY_STMT);
let callDecl = callable_arg_function_decl(gimple_call_fn(call));
callMsg = [callDecl, "declared here"];
callName = " '" + decl_name(callDecl) + "'";
}
catch (e if e.TreeCheckError) { }
this.warn([this.findReturnStmt(ss), "outparam '" + expr_display(v) + "' not written on NS_SUCCEEDED(return value)"],
[v, "outparam declared here"],
[blameStmt, "possibly written by unannotated function call" + callName],
callMsg);
} else {
this.logResult('succ', '', 'ok');
}
}
}
OutparamCheck.prototype.checkSubstateFailure = function(ss) {
for (let i = 0; i < this.psem_list.length; ++i) {
let [v, ps] = [ this.outparam_list[i], this.psem_list[i] ];
let val = ss.get(v);
if (val == av.WRITTEN) {
this.logResult('fail', 'written', 'error');
if (WARN_ON_SET_FAILURE) {
this.warn([this.findReturnStmt(ss), "outparam '" + expr_display(v) + "' written on NS_FAILED(return value)"],
[v, "outparam declared here"],
[ss.getBlame(v), "written here"]);
}
} else if (val == av.WROTE_NULL) {
this.logResult('fail', 'wrote_null', 'warning');
if (WARN_ON_SET_NULL) {
this.warn([this.findReturnStmt(ss), "NULL written to outparam '" + expr_display(v) + "' on NS_FAILED(return value)"],
[v, "outparam declared here"],
[ss.getBlame(v), "written here"]);
}
} else {
this.logResult('fail', '', 'ok');
}
}
}
/**
* Generate a warning from one or more tuples [treeforloc, message]
*/
OutparamCheck.prototype.warn = function(arg0) {
let loc = safe_location_of(arg0[0]);
let msg = arg0[1];
for (let i = 1; i < arguments.length; ++i) {
if (arguments[i] === undefined) continue;
let [atree, amsg] = arguments[i];
msg += "\n" + loc_string(safe_location_of(atree)) + ": " + amsg;
}
warning(msg, loc);
}
OutparamCheck.prototype.logResult = function(rv, msg, kind) {
if (LOG_RESULTS) {
let s = [ '"' + x + '"' for each (x in [ loc_string(location_of(this.fndecl)), function_decl_name(this.fndecl), rv, msg, kind ]) ].join(', ');
print(":LR: (" + s + ")");
}
}
// Parameter Semantics values -- indicates whether a parameter is
// an outparam.
// label Used for debugging output
// val Abstract value (state) that holds on an argument after
// a call
// check True if parameters with this semantics should be
// checked by this analysis
let ps = {
OUTNOFAIL: { label: 'out-no-fail', val: av.WRITTEN, check: true },
// Special value for receiver of strings methods. Callers should
// consider this to be an outparam (i.e., it modifies the string),
// but we don't want to check the method itself.
OUTNOFAILNOCHECK: { label: 'out-no-fail-no-check' },
OUT: { label: 'out', val: av.WRITTEN, check: true },
INOUT: { label: 'inout', val: av.WRITTEN, check: true },
MAYBE: { label: 'maybe', val: av.MAYBE_WRITTEN}, // maybe out
CONST: { label: 'const' } // i.e. not out
};
// Return the param semantics of a FUNCTION_DECL or VAR_DECL representing
// a function pointer. The result is a pair [ ann, sems ].
OutparamCheck.prototype.func_param_semantics = function(callable) {
let ftype = TREE_TYPE(callable);
if (TREE_CODE(ftype) == POINTER_TYPE) ftype = TREE_TYPE(ftype);
// What failure semantics to use for outparams
let rtype = TREE_TYPE(ftype);
let nofail = TREE_CODE(rtype) == VOID_TYPE;
// Whether to guess outparams by type
let guess = type_string(rtype) == 'nsresult';
// Set up param lists for analysis
let params; // param decls, if available
let types; // param types
let string_mutator = false;
if (TREE_CODE(callable) == FUNCTION_DECL) {
params = [ p for (p in function_decl_params(callable)) ];
types = [ TREE_TYPE(p) for each (p in params) ];
string_mutator = is_string_mutator(callable);
} else {
types = [ p for (p in function_type_args(ftype))
if (TREE_CODE(p) != VOID_TYPE) ];
}
// Analyze params
let ans = [];
for (let i = 0; i < types.length; ++i) {
let sem;
if (i == 0 && string_mutator) {
// Special case: string mutator receiver is an no-fail outparams
// but not checkable
sem = ps.OUTNOFAILNOCHECK;
} else {
if (params) sem = decode_attr(DECL_ATTRIBUTES(params[i]));
if (TRACE_CALL_SEM >= 2) print("param " + i + ": annotated " + sem);
if (sem == undefined) {
sem = decode_attr(TYPE_ATTRIBUTES(types[i]));
if (TRACE_CALL_SEM >= 2) print("type " + i + ": annotated " + sem);
if (sem == undefined) {
if (guess && type_is_outparam(types[i])) {
// Params other than last are guessed as MAYBE
sem = i < types.length - 1 ? ps.MAYBE : ps.OUT;
} else {
sem = ps.CONST;
}
}
}
if (sem == ps.OUT && nofail) sem = ps.OUTNOFAIL;
}
if (sem == undefined) throw new Error("assert");
ans.push(sem);
}
return ans;
}
/* Decode parameter semantics GCC attributes.
* @param attrs GCC attributes of a parameter. E.g., TYPE_ATTRIBUTES
* or DECL_ATTRIBUTES of an item
* @return The parameter semantics value defined by the attributes,
* or undefined if no such attributes were present. */
function decode_attr(attrs) {
// Note: we're not checking for conflicts, we just take the first
// one we find.
for each (let attr in rectify_attributes(attrs)) {
if (attr.name == 'user') {
for each (let arg in attr.args) {
if (arg == 'NS_outparam') {
return ps.OUT;
} else if (arg == 'NS_inoutparam') {
return ps.INOUT;
} else if (arg == 'NS_inparam') {
return ps.CONST;
}
}
}
}
return undefined;
}
/* @return true if the given type appears to be an outparam
* type based on the type alone (i.e., not considering
* attributes. */
function type_is_outparam(type) {
switch (TREE_CODE(type)) {
case POINTER_TYPE:
return pointer_type_is_outparam(TREE_TYPE(type));
case REFERENCE_TYPE:
let rt = TREE_TYPE(type);
return !TYPE_READONLY(rt) && is_string_type(rt);
default:
// Note: This is unsound for UNION_TYPE, because the union could
// contain a pointer.
return false;
}
}
/* Helper for type_is_outparam.
* @return true if 'pt *' looks like an outparam type. */
function pointer_type_is_outparam(pt) {
if (TYPE_READONLY(pt)) return false;
switch (TREE_CODE(pt)) {
case POINTER_TYPE:
case ARRAY_TYPE: {
// Look for void **, nsIFoo **, char **, PRUnichar **
let ppt = TREE_TYPE(pt);
let tname = TYPE_NAME(ppt);
if (tname == undefined) return false;
let name = decl_name_string(tname);
return name == 'void' || name == 'char' || name == 'PRUnichar' ||
name.substr(0, 3) == 'nsI';
}
case INTEGER_TYPE: {
// char * and PRUnichar * are probably strings, otherwise guess
// it is an integer outparam.
let name = decl_name_string(TYPE_NAME(pt));
return name != 'char' && name != 'PRUnichar';
}
case ENUMERAL_TYPE:
case REAL_TYPE:
case UNION_TYPE:
case BOOLEAN_TYPE:
return true;
case RECORD_TYPE:
// TODO: should we consider field writes?
return false;
case FUNCTION_TYPE:
case VOID_TYPE:
return false;
default:
throw new Error("can't guess if a pointer to this type is an outparam: " +
TREE_CODE(pt) + ': ' + type_string(pt));
}
}
// Map type name to boolean as to whether it is a string.
let cached_string_types = MapFactory.create_map(
function (x, y) x == y,
function (x) x,
function (t) t,
function (t) t);
// Base string types. Others will be found by searching the inheritance
// graph.
cached_string_types.put('nsAString', true);
cached_string_types.put('nsACString', true);
cached_string_types.put('nsAString_internal', true);
cached_string_types.put('nsACString_internal', true);
// Return true if the given type represents a Mozilla string type.
// The binfo arg is the binfo to use for further iteration. This is
// for internal use only, users of this function should pass only
// one arg.
function is_string_type(type, binfo) {
if (TREE_CODE(type) != RECORD_TYPE) return false;
//print(">>>IST " + type_string(type));
let name = decl_name_string(TYPE_NAME(type));
let ans = cached_string_types.get(name);
if (ans != undefined) return ans;
ans = false;
binfo = binfo != undefined ? binfo : TYPE_BINFO(type);
if (binfo != undefined) {
for each (let base in VEC_iterate(BINFO_BASE_BINFOS(binfo))) {
let parent_ans = is_string_type(BINFO_TYPE(base), base);
if (parent_ans) {
ans = true;
break;
}
}
}
cached_string_types.put(name, ans);
//print("<<<IST " + type_string(type) + ' ' + ans);
return ans;
}
function is_string_ptr_type(type) {
return TREE_CODE(type) == POINTER_TYPE && is_string_type(TREE_TYPE(type));
}
// Return true if the given function is a mutator method of a Mozilla
// string type.
function is_string_mutator(fndecl) {
let first_param = function() {
for (let p in function_decl_params(fndecl)) {
return p;
}
return undefined;
}();
return first_param != undefined &&
decl_name_string(first_param) == 'this' &&
is_string_ptr_type(TREE_TYPE(first_param)) &&
!TYPE_READONLY(TREE_TYPE(TREE_TYPE(first_param)));
}

Просмотреть файл

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

Просмотреть файл

@ -1,58 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Detects static initializers i.e. functions called during static initialization.
*/
require({ after_gcc_pass: "cfg" });
function process_tree(fn) {
for each (let attr in translate_attributes(DECL_ATTRIBUTES(fn)))
if (attr.name == "constructor")
warning(pretty_func(fn) + " marked with constructor attribute\n");
if (decl_name_string(fn) != "__static_initialization_and_destruction_0")
return;
let cfg = function_decl_cfg(fn);
for (let isn in cfg_isn_iterator(cfg)) {
if (isn.tree_code() != GIMPLE_CALL)
continue;
let decl = gimple_call_fndecl(isn);
let lhs = gimple_call_lhs(isn);
if (lhs) {
warning(pretty_var(lhs) + " defined by call to " + pretty_func(decl) +
" during static initialization", location_of(lhs));
} else {
let arg = constructorArg(isn);
if (arg)
warning(pretty_var(arg) + " defined by call to constructor " + pretty_func(decl) +
" during static initialization", location_of(arg));
else
warning(pretty_func(decl) + " called during static initialization", location_of(decl));
}
}
}
function constructorArg(call) {
let decl = gimple_call_fndecl(call);
if (!DECL_CONSTRUCTOR_P(decl))
return null;
let arg = gimple_call_arg_iterator(call).next();
if (TYPE_MAIN_VARIANT(TREE_TYPE(TREE_TYPE(arg))) != DECL_CONTEXT(decl))
throw new Error("malformed constructor call?!");
return arg.tree_code() == ADDR_EXPR ? TREE_OPERAND(arg, 0) : arg;
}
function pretty_func(fn) {
return rfunc_string(rectify_function_decl(fn));
}
function pretty_var(v) {
return type_string(TREE_TYPE(v)) + " " + expr_display(v);
}

Просмотреть файл

@ -1,7 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsString.h"
/* do nothing else */

Просмотреть файл

@ -1,402 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let dumpTypes = options['dump-types'].split(',');
let interestingList = {};
let typelist = {};
function interestingType(t)
{
let name = t.name;
if (dumpTypes.some(function(n) n == name)) {
interestingList[name] = t;
typelist[name] = t;
return true;
}
for each (let base in t.bases) {
if (base.access == 'public' && interestingType(base.type)) {
typelist[name] = t;
return true;
}
}
return false;
}
function addSubtype(t, subt)
{
if (subt.typedef === undefined &&
subt.kind === undefined)
throw Error("Unexpected subtype: not class or typedef: " + subt);
if (t.subtypes === undefined)
t.subtypes = [];
t.subtypes.push(subt);
}
function process_type(t)
{
interestingType(t);
for each (let base in t.bases)
addSubtype(base.type, t);
}
function process_decl(d)
{
if (d.typedef !== undefined && d.memberOf)
addSubtype(d.memberOf, d);
}
function publicBases(t)
{
yield t;
for each (let base in t.bases)
if (base.access == "public")
for each (let gbase in publicBases(base.type))
yield gbase;
}
function publicMembers(t)
{
for each (let base in publicBases(t)) {
for each (let member in base.members) {
if (member.access === undefined)
throw Error("Harumph: member without access? " + member);
if (member.access != "public")
continue;
yield member;
}
}
}
/**
* Get the short name of a decl name. E.g. turn
* "MyNamespace::MyClass::Method(int j) const" into
* "Method"
*/
function getShortName(decl)
{
let name = decl.name;
let lp = name.lastIndexOf('(');
if (lp != -1)
name = name.slice(0, lp);
lp = name.lastIndexOf('::');
if (lp != -1)
name = name.slice(lp + 2);
return name;
}
/**
* Remove functions in a base class which were overridden in a derived
* class.
*
* Although really, we should perhaps do this the other way around, or even
* group the two together, but that can come later.
*/
function removeOverrides(members)
{
let overrideMap = {};
for (let i = members.length - 1; i >= 0; --i) {
let m = members[i];
if (!m.isFunction)
continue;
let shortName = getShortName(m);
let overrides = overrideMap[shortName];
if (overrides === undefined) {
overrideMap[shortName] = [m];
continue;
}
let found = false;
for each (let override in overrides) {
if (signaturesMatch(override, m)) {
// remove members[i], it was overridden
members.splice(i, 1);
found = true;
}
}
if (found)
continue;
overrides.push(m);
}
}
/**
* Generates the starting position of lines within a file.
*/
function getLineLocations(fdata)
{
yield 0;
let r = /\n/y;
let pos = 0;
let i = 1;
for (;;) {
pos = fdata.indexOf('\n', pos) + 1;
if (pos == 0)
break;
yield pos;
i++;
}
}
/**
* Find and return the doxygen comment immediately prior to the location
* object that was passed in.
*
* @todo: parse doccomment data such as @param, @returns
* @todo: parse comments for markup
*/
function getDocComment(loc)
{
let fdata = read_file(loc.file);
let linemap = [l for (l in getLineLocations(fdata))];
if (loc.line >= linemap.length) {
warning("Location larger than actual header: " + loc);
return <></>;
}
let endpos = linemap[loc.line - 1] + loc.column - 1;
let semipos = fdata.lastIndexOf(';', endpos);
let bracepos = fdata.lastIndexOf('}', endpos);
let searchslice = fdata.slice(Math.max(semipos, bracepos) + 1, endpos);
let m = searchslice.match(/\/\*\*[\s\S]*?\*\//gm);
if (m === null)
return <></>;
let dc = m[m.length - 1].slice(3, -2);
dc = dc.replace(/^\s*(\*+[ \t]*)?/gm, "");
return <pre class="doccomment">{dc}</pre>;
}
function typeName(t)
{
if (t.name !== undefined)
return t.name;
if (t.isPointer)
return "%s%s*".format(t.isConst ? "const " : "", typeName(t.type));
if (t.isReference)
return "%s%s&".format(t.isConst ? "const " : "", typeName(t.type));
return t.toString();
}
function publicBaseList(t)
{
let l = <ul/>;
for each (let b in t.bases) {
if (b.access == 'public')
l.* += <li><a href={"/en/%s".format(b.type.name)}>{b.type.name}</a></li>;
}
if (l.*.length() == 0)
return <></>;
return <>
<h2>Base Classes</h2>
{l}
</>;
}
/**
* Get a source-link for a given location.
*/
function getLocLink(loc, text)
{
return <a class="loc"
href={"http://hg.mozilla.org/mozilla-central/file/%s/[LOC%s]#l%i".format(options.rev, loc.file, loc.line)}>{text}</a>;
}
function dumpType(t)
{
print("DUMP-TYPE(%s)".format(t.name));
let methodOverview = <tbody />;
let methodList = <div/>;
let memberList = <></>;
let shortNameMap = {};
let members = [m for (m in publicMembers(t))];
removeOverrides(members);
for each (let m in members) {
let qname = m.memberOf.name + '::';
// we don't inherit constructors from base classes
if (m.isConstructor && m.memberOf !== t)
continue;
if (m.name.indexOf(qname) != 0)
throw Error("Member name not qualified?");
let name = m.name.slice(qname.length);
if (name.indexOf('~') == 0)
continue;
if (m.isFunction) {
let innerList;
let shortName = getShortName(m);
if (m.isConstructor)
shortName = 'Constructors';
if (shortNameMap.hasOwnProperty(shortName)) {
innerList = shortNameMap[shortName];
}
else {
let overview =
<tr><td>
<a href={'#%s'.format(escape(shortName))}>{shortName}</a>
</td></tr>;
if (m.isConstructor)
methodOverview.insertChildAfter(null, overview);
else
methodOverview.appendChild(overview);
let shortMarkup =
<div>
<h3 id={shortName}>{shortName}</h3>
<dl/>
</div>;
if (m.isConstructor)
methodList.insertChildAfter(null, shortMarkup);
else
methodList.appendChild(shortMarkup);
innerList = shortMarkup.dl;
shortNameMap[shortName] = innerList;
}
let parameters = <ul/>;
for each (p in m.parameters) {
let name = p.name;
if (name == 'this')
continue;
if (/^D_\d+$/.test(name))
name = '<anonymous>';
parameters.* += <li>{typeName(p.type)} {name}</li>;
}
innerList.* +=
<>
<dt id={name} class="methodName">
<code>{typeName(m.type.type)} {name}</code> - {getLocLink(m.loc, "source")}
</dt>
<dd>
{getDocComment(m.loc)}
{parameters.*.length() > 0 ?
<>
<h4>Parameters</h4>
{parameters}
</> : <></>}
</dd>
</>;
}
else {
memberList += <li class="member">{name}</li>;
}
}
let r =
<body>
<p>{getLocLink(t.loc, "Class Declaration")}</p>
{getDocComment(t.loc)}
{dumpTypes.some(function(n) n == t.name) ?
<>
[MAP{t.name}-graph.map]
<img src={"/@api/deki/pages/=en%%252F%s/files/=%s-graph.png".format(t.name, t.name)} usemap="#classes" />
</> : <></>
}
{methodOverview.*.length() > 0 ?
<>
<h2>Method Overview</h2>
<table class="standard-table">{methodOverview}</table>
</> :
""
}
{publicBaseList(t)}
<h2>Data Members</h2>
{memberList.*.length() > 0 ?
memberList :
<p><em>No public members.</em></p>
}
<h2>Methods</h2>
{methodList.*.length() > 0 ?
methodList :
<p><em>No public methods.</em></p>
}
</body>;
write_file(t.name + ".html", r.toXMLString());
}
function graphType(t)
{
print("GRAPH-TYPE(%s)".format(t.name));
let contents = "digraph classes {\n"
+ " node [shape=rectangle fontsize=11]\n"
+ " %s;\n".format(t.name);
function graphClass(c)
{
contents += '%s [URL="http://developer.mozilla.org/en/%s"]\n'.format(c.name, c.name);
for each (let st in c.subtypes) {
contents += " %s -> %s;\n".format(c.name, st.name);
graphClass(st);
}
}
graphClass(t);
contents += "}\n";
write_file(t.name + "-graph.gv", contents);
}
function input_end()
{
for (let p in typelist)
dumpType(typelist[p]);
for (let n in interestingList)
graphType(interestingList[n]);
}

Просмотреть файл

@ -38,9 +38,6 @@ TEST_TOOL_DIRS += [
# 'reflect/xptcall/tests,
#]
if CONFIG['DEHYDRA_PATH']:
DIRS += ['analysis']
CONFIGURE_DEFINE_FILES += [
'xpcom-config.h',
'xpcom-private.h',