self.parent.end_pos = pos
return LinearControlFlow(pos, self.parent)
- def get_state(self, pos, item):
- return self.get_pos_state(pos, item)[1]
+ def get_state(self, item, pos=((),())):
+ return self.get_pos_state(item, pos)[1]
- def get_pos_state(self, pos, item):
+ def get_pos_state(self, item, pos=((),())):
# do some caching
if pos > self.end_pos:
try:
return self.tip[item]
except KeyError:
- self.tip[item] = pos_state = self._get_pos_state(pos, item)
+ self.tip[item] = pos_state = self._get_pos_state(item, pos)
return pos_state
else:
- return self._get_pos_state(pos, item)
+ return self._get_pos_state(item, pos)
class LinearControlFlow(ControlFlow):
bisect.insort(event_list, (pos, state))
- def _get_pos_state(self, pos, item):
+ def _get_pos_state(self, item, pos):
if pos > self.start_pos:
if self.events.has_key(item):
event_list = self.events[item]
return event
if self.incoming is not None:
- return self.incoming.get_pos_state(pos, item)
+ return self.incoming.get_pos_state(item, pos)
else:
return None, None
- def to_string(self, indent, limit=None):
+ def to_string(self, indent='', limit=None):
if len(self.events) == 0:
s = indent + "[no state changes]"
branch.set_state(pos, item, state)
return
- def _get_pos_state(self, pos, item):
+ def _get_pos_state(self, item, pos):
if pos <= self.start_pos:
- return self.incoming.get_pos_state(pos, item)
+ return self.incoming.get_pos_state(item, pos)
elif pos < self.end_pos:
for branch_pos, branch in zip(self.branch_starts[::-1], self.branches[::-1]):
if pos >= branch_pos:
- return branch.get_pos_state(pos, item)
+ return branch.get_pos_state(item, pos)
else:
- last_pos, last_state = self.branches[0].get_pos_state(pos, item)
+ last_pos, last_state = self.branches[0].get_pos_state(item, pos)
if last_state is None:
return None, None
for branch in self.branches[1:]:
- other_pos, other_state = branch.get_pos_state(pos, item)
+ other_pos, other_state = branch.get_pos_state(item, pos)
if other_state is None or other_state != last_state:
return None, None
elif last_pos is not other_pos:
self.branch_starts.append(pos)
return self.branches[-1]
- def to_string(self, indent, limit=None):
+ def to_string(self, indent='', limit=None):
join = "\n%sor\n" % indent
s = join.join([branch.to_string(indent+" ", limit=self.incoming) for branch in self.branches])
if self.incoming is not limit and self.incoming is not None:
if not self.entry:
self.entry = env.declare_var(self.name, py_object_type, self.pos)
env.control_flow.set_state(self.pos, (self.name, 'initalized'), True)
+ env.control_flow.set_state(self.pos, (self.name, 'source'), 'assignment')
if self.entry.is_declared_generic:
self.result_ctype = py_object_type
self.entry.name,
code.error_goto_if_null(self.result_code, self.pos)))
elif entry.is_local:
- assigned = entry.scope.control_flow.get_state(self.pos, (entry.name, 'initalized'))
+ assigned = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
if assigned is False:
error(self.pos, "local variable '%s' referenced before assignment" % entry.name)
- elif assigned is None:
- code.putln('/* check %s */' % entry.cname)
+ elif not Options.init_local_none and assigned is None:
+ code.putln('if (%s == 0) { PyErr_SetString(PyExc_UnboundLocalError, "%s"); %s }' % (entry.cname, entry.name, code.error_goto(self.pos)))
+ entry.scope.control_flow.set_state(self.pos, (entry.name, 'initalized'), True)
def generate_assignment_code(self, rhs, code):
#print "NameNode.generate_assignment_code:", self.name ###
#print "...LHS type", self.type, "ctype", self.ctype() ###
#print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
rhs.make_owned_reference(code)
- code.put_decref(self.result_code, self.ctype())
+ if entry.is_local and not Options.init_local_none:
+ initalized = entry.scope.control_flow.get_state((entry.name, 'initalized'), self.pos)
+ if initalized is True:
+ code.put_decref(self.result_code, self.ctype())
+ elif initalized is None:
+ code.put_xdecref(self.result_code, self.ctype())
+ else:
+ code.put_decref(self.result_code, self.ctype())
code.putln('%s = %s;' % (self.result_code, rhs.result_as(self.ctype())))
if debug_disposal_code:
print("NameNode.generate_assignment_code:")
self.entry = env.lookup(self.name)
if self.rhs is not None:
env.control_flow.set_state(self.rhs.end_pos(), (self.entry.name, 'initalized'), True)
+ env.control_flow.set_state(self.rhs.end_pos(), (self.entry.name, 'source'), 'assignment')
self.entry.used = 1
if self.type.is_pyobject:
self.entry.init_to_none = False
code.putln("PyGILState_STATE _save = PyGILState_Ensure();")
# ----- Fetch arguments
self.generate_argument_parsing_code(env, code)
- self.generate_argument_increfs(lenv, code)
+ # If an argument is assigned to in the body, we must
+ # incref it to properly keep track of refcounts.
+ for entry in lenv.arg_entries:
+ if entry.type.is_pyobject and lenv.control_flow.get_state((entry.name, 'source')) != 'arg':
+ code.put_var_incref(entry)
# ----- Initialise local variables
for entry in lenv.var_entries:
if entry.type.is_pyobject and entry.init_to_none and entry.used:
#self.return_type.default_value))
# ----- Return cleanup
code.put_label(code.return_label)
+ if not Options.init_local_none:
+ for entry in lenv.var_entries:
+ print entry.name, lenv.control_flow.get_state((entry.name, 'initalized'))
+ if lenv.control_flow.get_state((entry.name, 'initalized')) is not True:
+ entry.xdecref_cleanup = 1
code.put_var_decrefs(lenv.var_entries, used_only = 1)
+ # Decref any increfed args
for entry in lenv.arg_entries:
-# if len(entry.assignments) > 0:
+ if entry.type.is_pyobject and lenv.control_flow.get_state((entry.name, 'source')) != 'arg':
code.put_var_decref(entry)
self.put_stararg_decrefs(code)
if acquire_gil:
if self.py_func:
self.py_func.generate_function_definitions(env, code, transforms)
self.generate_optarg_wrapper_function(env, code)
-
+
def put_stararg_decrefs(self, code):
pass
error(arg.pos,
"Argument type '%s' is incomplete" % arg.type)
return env.declare_arg(arg.name, arg.type, arg.pos)
-
- def generate_argument_increfs(self, env, code):
- # Turn borrowed argument refs into owned refs.
- # This is necessary, because if the argument is
- # assigned to, it will be decrefed.
- for entry in env.arg_entries:
-# if len(entry.assignments) > 0:
- code.put_var_incref(entry)
-
def generate_optarg_wrapper_function(self, env, code):
pass
elif not code.continue_label:
error(self.pos, "continue statement not inside loop")
else:
- #code.putln(
- # "goto %s;" %
- # code.continue_label)
code.put_goto(code.continue_label)
def analyse_control_flow(self, env):
env.start_branching(self.pos)
self.body.analyse_control_flow(env)
+ successful_try = env.control_flow # grab this for later
+ env.next_branch(self.body.end_pos())
+ env.finish_branching(self.body.end_pos())
+
+ env.start_branching(self.except_clauses[0].pos)
for except_clause in self.except_clauses:
- env.next_branch(except_clause.pos)
except_clause.analyse_control_flow(env)
+ env.next_branch(except_clause.end_pos())
+
+ # the else cause it executed only when the try clause finishes
+ env.control_flow.incoming = successful_try
if self.else_clause:
- env.next_branch(self.except_clauses[-1].end_pos())
- # the else cause it executed only when the try clause finishes
- env.control_flow.incoming = env.control_flow.parent.branches[0]
self.else_clause.analyse_control_flow(env)
env.finish_branching(self.end_pos())
env.start_branching(self.pos)
self.body.analyse_control_flow(env)
env.next_branch(self.body.end_pos())
+ env.finish_branching(self.body.end_pos())
self.finally_clause.analyse_control_flow(env)
- env.finish_branching(self.end_pos())
def analyse_declarations(self, env):
self.body.analyse_declarations(env)
# lookup on every call.
# If this is 0 it simply creates a wrapper.
lookup_module_cpdef = 0
+
+# This will set local variables to None rather than NULL which may cause
+# surpress what would be an UnboundLocalError in pure Python but eliminates
+# checking for NULL on every use, and can decref rather than xdecref at the end.
+# WARNING: This is a work in progress, may currently segfault.
+init_local_none = 1
entry.is_arg = 1
#entry.borrowed = 1 # Not using borrowed arg refs for now
self.arg_entries.append(entry)
+ self.control_flow.set_state((), (name, 'source'), 'arg')
return entry
def declare_var(self, name, type, pos,
error(pos, "Local variable cannot be declared %s" % visibility)
entry = Scope.declare_var(self, name, type, pos,
cname, visibility, is_cdef)
- entry.init_to_none = type.is_pyobject
+ if type.is_pyobject and not Options.init_local_none:
+ entry.init = "0"
+ entry.init_to_none = type.is_pyobject and Options.init_local_none
entry.is_local = 1
self.var_entries.append(entry)
return entry