Lichen

Annotated inspector.py

343:e0879c83a439
2016-12-07 Paul Boddie Added support for reading to the end of a stream's input, fixing EOFError raising in fread by returning shorter amounts of data when EOF occurs, only raising an exception if no data was read before EOF occurred. Made the test input longer to exercise tests of reading remaining data.
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@0 3
"""
paul@0 4
Inspect and obtain module structure.
paul@0 5
paul@0 6
Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013,
paul@0 7
              2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
paul@0 8
paul@0 9
This program is free software; you can redistribute it and/or modify it under
paul@0 10
the terms of the GNU General Public License as published by the Free Software
paul@0 11
Foundation; either version 3 of the License, or (at your option) any later
paul@0 12
version.
paul@0 13
paul@0 14
This program is distributed in the hope that it will be useful, but WITHOUT
paul@0 15
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@0 16
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@0 17
details.
paul@0 18
paul@0 19
You should have received a copy of the GNU General Public License along with
paul@0 20
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@0 21
"""
paul@0 22
paul@0 23
from branching import BranchTracker
paul@110 24
from common import CommonModule, get_argnames, init_item, predefined_constants
paul@26 25
from modules import BasicModule, CacheWritingModule, InspectionNaming
paul@3 26
from errors import InspectError
paul@0 27
from referencing import Reference
paul@12 28
from resolving import NameResolving
paul@12 29
from results import AccessRef, InstanceRef, InvocationRef, LiteralSequenceRef, \
paul@226 30
                    LocalNameRef, NameRef, ResolvedNameRef, VariableRef
paul@0 31
import compiler
paul@0 32
import sys
paul@0 33
paul@26 34
class InspectedModule(BasicModule, CacheWritingModule, NameResolving, InspectionNaming):
paul@0 35
paul@0 36
    "A module inspector."
paul@0 37
paul@0 38
    def __init__(self, name, importer):
paul@13 39
paul@13 40
        "Initialise the module with basic details."
paul@13 41
paul@0 42
        BasicModule.__init__(self, name, importer)
paul@12 43
paul@0 44
        self.in_class = False
paul@0 45
        self.in_conditional = False
paul@110 46
paul@110 47
        # Accesses to global attributes.
paul@110 48
paul@0 49
        self.global_attr_accesses = {}
paul@0 50
paul@0 51
        # Usage tracking.
paul@0 52
paul@0 53
        self.trackers = []
paul@0 54
        self.attr_accessor_branches = {}
paul@0 55
paul@0 56
    def __repr__(self):
paul@0 57
        return "InspectedModule(%r, %r)" % (self.name, self.importer)
paul@0 58
paul@27 59
    # Principal methods.
paul@27 60
paul@0 61
    def parse(self, filename):
paul@0 62
paul@0 63
        "Parse the file having the given 'filename'."
paul@0 64
paul@0 65
        self.parse_file(filename)
paul@0 66
paul@0 67
        # Inspect the module.
paul@0 68
paul@0 69
        self.start_tracking_in_module()
paul@0 70
paul@0 71
        # Detect and record imports and globals declared in the module.
paul@0 72
paul@0 73
        self.process_structure(self.astnode)
paul@0 74
paul@0 75
        # Set the class of the module after the definition has occurred.
paul@0 76
paul@269 77
        ref = self.get_builtin("module")
paul@0 78
        self.set_name("__class__", ref)
paul@271 79
        self.set_name("__mname__", self.get_constant("string", self.name).reference())
paul@271 80
        self.set_name("__file__", self.get_constant("string", filename).reference())
paul@0 81
paul@0 82
        # Get module-level attribute usage details.
paul@0 83
paul@0 84
        self.stop_tracking_in_module()
paul@0 85
paul@27 86
        # Collect external name references.
paul@0 87
paul@27 88
        self.collect_names()
paul@0 89
paul@12 90
    def complete(self):
paul@0 91
paul@12 92
        "Complete the module inspection."
paul@0 93
paul@12 94
        # Resolve names not definitively mapped to objects.
paul@0 95
paul@12 96
        self.resolve()
paul@0 97
paul@12 98
        # Define the invocation requirements in each namespace.
paul@0 99
paul@12 100
        self.set_invocation_usage()
paul@0 101
paul@12 102
        # Propagate to the importer information needed in subsequent activities.
paul@0 103
paul@12 104
        self.propagate()
paul@0 105
paul@27 106
    # Accessory methods.
paul@0 107
paul@27 108
    def collect_names(self):
paul@0 109
paul@27 110
        "Collect the names used by each scope."
paul@0 111
paul@0 112
        for path in self.names_used.keys():
paul@27 113
            self.collect_names_for_path(path)
paul@27 114
paul@27 115
    def collect_names_for_path(self, path):
paul@0 116
paul@33 117
        """
paul@33 118
        Collect the names used by the given 'path'. These are propagated to the
paul@33 119
        importer in advance of any dependency resolution.
paul@33 120
        """
paul@0 121
paul@0 122
        names = self.names_used.get(path)
paul@0 123
        if not names:
paul@0 124
            return
paul@0 125
paul@0 126
        in_function = self.function_locals.has_key(path)
paul@0 127
paul@0 128
        for name in names:
paul@135 129
            if in_function and name in self.function_locals[path]:
paul@135 130
                continue
paul@135 131
paul@135 132
            key = "%s.%s" % (path, name)
paul@135 133
paul@135 134
            # Find predefined constant names before anything else.
paul@135 135
paul@135 136
            if name in predefined_constants:
paul@135 137
                ref = self.get_builtin(name)
paul@135 138
                self.set_name_reference(key, ref)
paul@0 139
                continue
paul@0 140
paul@35 141
            # Find local definitions (within dynamic namespaces).
paul@0 142
paul@27 143
            ref = self.get_resolved_object(key)
paul@0 144
            if ref:
paul@40 145
                self.set_name_reference(key, ref)
paul@0 146
                continue
paul@0 147
paul@142 148
            # Find global.
paul@0 149
paul@142 150
            ref = self.get_global(name)
paul@27 151
            if ref:
paul@40 152
                self.set_name_reference(key, ref)
paul@0 153
                continue
paul@0 154
paul@40 155
            # Find presumed built-in definitions.
paul@0 156
paul@40 157
            ref = self.get_builtin(name)
paul@40 158
            self.set_name_reference(key, ref)
paul@0 159
paul@40 160
    def set_name_reference(self, path, ref):
paul@0 161
paul@40 162
        "Map the given name 'path' to 'ref'."
paul@0 163
paul@40 164
        self.importer.all_name_references[path] = self.name_references[path] = ref
paul@0 165
paul@0 166
    # Module structure traversal.
paul@0 167
paul@0 168
    def process_structure_node(self, n):
paul@0 169
paul@0 170
        "Process the individual node 'n'."
paul@0 171
paul@205 172
        path = self.get_namespace_path()
paul@205 173
paul@0 174
        # Module global detection.
paul@0 175
paul@0 176
        if isinstance(n, compiler.ast.Global):
paul@0 177
            self.process_global_node(n)
paul@0 178
paul@0 179
        # Module import declarations.
paul@0 180
paul@0 181
        elif isinstance(n, compiler.ast.From):
paul@0 182
            self.process_from_node(n)
paul@0 183
paul@0 184
        elif isinstance(n, compiler.ast.Import):
paul@0 185
            self.process_import_node(n)
paul@0 186
paul@0 187
        # Nodes using operator module functions.
paul@0 188
paul@0 189
        elif isinstance(n, compiler.ast.Operator):
paul@0 190
            return self.process_operator_node(n)
paul@0 191
paul@0 192
        elif isinstance(n, compiler.ast.AugAssign):
paul@0 193
            self.process_augassign_node(n)
paul@0 194
paul@0 195
        elif isinstance(n, compiler.ast.Compare):
paul@0 196
            return self.process_compare_node(n)
paul@0 197
paul@0 198
        elif isinstance(n, compiler.ast.Slice):
paul@0 199
            return self.process_slice_node(n)
paul@0 200
paul@0 201
        elif isinstance(n, compiler.ast.Sliceobj):
paul@0 202
            return self.process_sliceobj_node(n)
paul@0 203
paul@0 204
        elif isinstance(n, compiler.ast.Subscript):
paul@0 205
            return self.process_subscript_node(n)
paul@0 206
paul@0 207
        # Namespaces within modules.
paul@0 208
paul@0 209
        elif isinstance(n, compiler.ast.Class):
paul@0 210
            self.process_class_node(n)
paul@0 211
paul@0 212
        elif isinstance(n, compiler.ast.Function):
paul@0 213
            self.process_function_node(n, n.name)
paul@0 214
paul@0 215
        elif isinstance(n, compiler.ast.Lambda):
paul@0 216
            return self.process_lambda_node(n)
paul@0 217
paul@0 218
        # Assignments.
paul@0 219
paul@0 220
        elif isinstance(n, compiler.ast.Assign):
paul@0 221
paul@0 222
            # Handle each assignment node.
paul@0 223
paul@0 224
            for node in n.nodes:
paul@0 225
                self.process_assignment_node(node, n.expr)
paul@0 226
paul@0 227
        # Assignments within non-Assign nodes.
paul@0 228
paul@0 229
        elif isinstance(n, compiler.ast.AssName):
paul@205 230
            raise InspectError("Name assignment appearing outside assignment statement.", path, n)
paul@0 231
paul@0 232
        elif isinstance(n, compiler.ast.AssAttr):
paul@205 233
            raise InspectError("Attribute assignment appearing outside assignment statement.", path, n)
paul@0 234
paul@0 235
        # Accesses.
paul@0 236
paul@0 237
        elif isinstance(n, compiler.ast.Getattr):
paul@0 238
            return self.process_attribute_access(n)
paul@0 239
paul@0 240
        # Name recording for later testing.
paul@0 241
paul@0 242
        elif isinstance(n, compiler.ast.Name):
paul@0 243
            return self.process_name_node(n)
paul@0 244
paul@0 245
        # Conditional statement tracking.
paul@0 246
paul@0 247
        elif isinstance(n, compiler.ast.For):
paul@0 248
            self.process_for_node(n)
paul@0 249
paul@0 250
        elif isinstance(n, compiler.ast.While):
paul@0 251
            self.process_while_node(n)
paul@0 252
paul@0 253
        elif isinstance(n, compiler.ast.If):
paul@0 254
            self.process_if_node(n)
paul@0 255
paul@0 256
        elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
paul@0 257
            return self.process_logical_node(n)
paul@0 258
paul@0 259
        # Exception control-flow tracking.
paul@0 260
paul@0 261
        elif isinstance(n, compiler.ast.TryExcept):
paul@0 262
            self.process_try_node(n)
paul@0 263
paul@0 264
        elif isinstance(n, compiler.ast.TryFinally):
paul@0 265
            self.process_try_finally_node(n)
paul@0 266
paul@0 267
        # Control-flow modification statements.
paul@0 268
paul@0 269
        elif isinstance(n, compiler.ast.Break):
paul@0 270
            self.trackers[-1].suspend_broken_branch()
paul@0 271
paul@0 272
        elif isinstance(n, compiler.ast.Continue):
paul@0 273
            self.trackers[-1].suspend_continuing_branch()
paul@0 274
paul@0 275
        elif isinstance(n, compiler.ast.Raise):
paul@0 276
            self.process_structure(n)
paul@0 277
            self.trackers[-1].abandon_branch()
paul@0 278
paul@0 279
        elif isinstance(n, compiler.ast.Return):
paul@0 280
            self.process_structure(n)
paul@0 281
            self.trackers[-1].abandon_returning_branch()
paul@0 282
paul@173 283
        # Print statements.
paul@173 284
paul@173 285
        elif isinstance(n, (compiler.ast.Print, compiler.ast.Printnl)):
paul@173 286
            self.process_print_node(n)
paul@173 287
paul@0 288
        # Invocations.
paul@0 289
paul@0 290
        elif isinstance(n, compiler.ast.CallFunc):
paul@0 291
            return self.process_invocation_node(n)
paul@0 292
paul@0 293
        # Constant usage.
paul@0 294
paul@0 295
        elif isinstance(n, compiler.ast.Const):
paul@188 296
            typename = n.value.__class__.__name__
paul@188 297
            return self.get_literal_instance(n, typename == "str" and "string" or typename)
paul@0 298
paul@0 299
        elif isinstance(n, compiler.ast.Dict):
paul@0 300
            return self.get_literal_instance(n, "dict")
paul@0 301
paul@0 302
        elif isinstance(n, compiler.ast.List):
paul@0 303
            return self.get_literal_instance(n, "list")
paul@0 304
paul@0 305
        elif isinstance(n, compiler.ast.Tuple):
paul@0 306
            return self.get_literal_instance(n, "tuple")
paul@0 307
paul@3 308
        # Unsupported nodes.
paul@3 309
paul@3 310
        elif isinstance(n, compiler.ast.GenExpr):
paul@205 311
            raise InspectError("Generator expressions are not supported.", path, n)
paul@3 312
paul@3 313
        elif isinstance(n, compiler.ast.IfExp):
paul@205 314
            raise InspectError("If-else expressions are not supported.", path, n)
paul@0 315
paul@0 316
        elif isinstance(n, compiler.ast.ListComp):
paul@205 317
            raise InspectError("List comprehensions are not supported.", path, n)
paul@0 318
paul@0 319
        # All other nodes are processed depth-first.
paul@0 320
paul@0 321
        else:
paul@0 322
            self.process_structure(n)
paul@0 323
paul@0 324
        # By default, no expression details are returned.
paul@0 325
paul@0 326
        return None
paul@0 327
paul@0 328
    # Specific node handling.
paul@0 329
paul@0 330
    def process_assignment_node(self, n, expr):
paul@0 331
paul@0 332
        "Process the individual node 'n' to be assigned the contents of 'expr'."
paul@0 333
paul@0 334
        # Names and attributes are assigned the entire expression.
paul@0 335
paul@0 336
        if isinstance(n, compiler.ast.AssName):
paul@61 337
            if n.name == "self":
paul@61 338
                raise InspectError("Redefinition of self is not allowed.", self.get_namespace_path(), n)
paul@0 339
paul@0 340
            name_ref = expr and self.process_structure_node(expr)
paul@0 341
paul@0 342
            # Name assignments populate either function namespaces or the
paul@0 343
            # general namespace hierarchy.
paul@0 344
paul@0 345
            self.assign_general_local(n.name, name_ref)
paul@0 346
paul@0 347
            # Record usage of the name.
paul@0 348
paul@0 349
            self.record_name(n.name)
paul@0 350
paul@0 351
        elif isinstance(n, compiler.ast.AssAttr):
paul@124 352
            if expr:
paul@124 353
                expr = self.process_structure_node(expr)
paul@107 354
paul@107 355
            in_assignment = self.in_assignment
paul@124 356
            self.in_assignment = expr
paul@0 357
            self.process_attribute_access(n)
paul@107 358
            self.in_assignment = in_assignment
paul@0 359
paul@0 360
        # Lists and tuples are matched against the expression and their
paul@0 361
        # items assigned to expression items.
paul@0 362
paul@0 363
        elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
paul@0 364
            self.process_assignment_node_items(n, expr)
paul@0 365
paul@0 366
        # Slices and subscripts are permitted within assignment nodes.
paul@0 367
paul@0 368
        elif isinstance(n, compiler.ast.Slice):
paul@0 369
            self.process_slice_node(n, expr)
paul@0 370
paul@0 371
        elif isinstance(n, compiler.ast.Subscript):
paul@0 372
            self.process_subscript_node(n, expr)
paul@0 373
paul@0 374
    def process_attribute_access(self, n):
paul@0 375
paul@0 376
        "Process the given attribute access node 'n'."
paul@0 377
paul@107 378
        # Obtain any completed chain and return the reference to it.
paul@107 379
paul@0 380
        name_ref = self.process_attribute_chain(n)
paul@107 381
paul@0 382
        if self.have_access_expression(n):
paul@0 383
            return name_ref
paul@0 384
paul@0 385
        # Where the start of the chain of attributes has been reached, determine
paul@0 386
        # the complete access.
paul@0 387
paul@0 388
        # Given a non-access node, this chain can be handled in its entirety,
paul@0 389
        # either being name-based and thus an access rooted on a name, or being
paul@0 390
        # based on some other node and thus an anonymous access of some kind.
paul@0 391
paul@0 392
        path = self.get_namespace_path()
paul@0 393
paul@0 394
        # Start with the the full attribute chain.
paul@0 395
paul@0 396
        remaining = self.attrs
paul@0 397
        attrnames = ".".join(remaining)
paul@0 398
paul@0 399
        # If the accessor cannot be identified, or where attributes
paul@0 400
        # remain in an attribute chain, record the anonymous accesses.
paul@0 401
paul@0 402
        if not isinstance(name_ref, NameRef): # includes ResolvedNameRef
paul@0 403
paul@0 404
            init_item(self.attr_accesses, path, set)
paul@0 405
            self.attr_accesses[path].add(attrnames)
paul@0 406
paul@117 407
            self.record_access_details(None, attrnames, self.in_assignment,
paul@117 408
                self.in_invocation)
paul@0 409
            del self.attrs[0]
paul@0 410
            return
paul@0 411
paul@0 412
        # Name-based accesses will handle the first attribute in a
paul@0 413
        # chain.
paul@0 414
paul@0 415
        else:
paul@0 416
            attrname = remaining[0]
paul@0 417
paul@0 418
            # Attribute assignments are used to identify instance attributes.
paul@0 419
paul@0 420
            if isinstance(n, compiler.ast.AssAttr) and \
paul@0 421
                self.in_class and self.in_function and n.expr.name == "self":
paul@0 422
paul@0 423
                self.set_instance_attr(attrname)
paul@0 424
paul@0 425
            # Record attribute usage using any name local to this namespace,
paul@0 426
            # if assigned in the namespace, or using an external name
paul@0 427
            # (presently just globals within classes).
paul@0 428
paul@0 429
            name = self.get_name_for_tracking(name_ref.name, name_ref.final())
paul@0 430
            tracker = self.trackers[-1]
paul@0 431
paul@0 432
            immediate_access = len(self.attrs) == 1
paul@0 433
            assignment = immediate_access and isinstance(n, compiler.ast.AssAttr)
paul@0 434
paul@0 435
            # Record global-based chains for subsequent resolution.
paul@0 436
paul@0 437
            is_global = self.in_function and not self.function_locals[path].has_key(name) or \
paul@0 438
                        not self.in_function
paul@0 439
paul@0 440
            if is_global:
paul@0 441
                self.record_global_access_details(name, attrnames)
paul@0 442
paul@0 443
            # Make sure the name is being tracked: global names will not
paul@0 444
            # already be initialised in a branch and must be added
paul@0 445
            # explicitly.
paul@0 446
paul@0 447
            if not tracker.have_name(name):
paul@0 448
                tracker.assign_names([name])
paul@0 449
                if self.in_function:
paul@0 450
                    self.scope_globals[path].add(name)
paul@0 451
paul@0 452
            # Record attribute usage in the tracker, and record the branch
paul@0 453
            # information for the access.
paul@0 454
paul@110 455
            branches = tracker.use_attribute(name, attrname, self.in_invocation, assignment)
paul@0 456
paul@0 457
            if not branches:
paul@84 458
                raise InspectError("Name %s is accessed using %s before an assignment." % (
paul@84 459
                    name, attrname), path, n)
paul@0 460
paul@0 461
            self.record_branches_for_access(branches, name, attrnames)
paul@117 462
            access_number = self.record_access_details(name, attrnames,
paul@117 463
                self.in_assignment, self.in_invocation)
paul@107 464
paul@107 465
            del self.attrs[0]
paul@0 466
            return AccessRef(name, attrnames, access_number)
paul@0 467
paul@0 468
    def process_class_node(self, n):
paul@0 469
paul@0 470
        "Process the given class node 'n'."
paul@0 471
paul@0 472
        path = self.get_namespace_path()
paul@0 473
paul@0 474
        # To avoid notions of class "versions" where the same definition
paul@0 475
        # might be parameterised with different state and be referenced
paul@0 476
        # elsewhere (as base classes, for example), classes in functions or
paul@0 477
        # conditions are forbidden.
paul@0 478
paul@0 479
        if self.in_function or self.in_conditional:
paul@0 480
            print >>sys.stderr, "In %s, class %s in function or conditional statement ignored." % (
paul@0 481
                path, n.name)
paul@0 482
            return
paul@0 483
paul@0 484
        # Resolve base classes.
paul@0 485
paul@0 486
        bases = []
paul@0 487
paul@0 488
        for base in n.bases:
paul@0 489
            base_class = self.get_class(base)
paul@0 490
paul@0 491
            if not base_class:
paul@12 492
                print >>sys.stderr, "In %s, class %s has unidentifiable base class: %s" % (
paul@12 493
                    path, n.name, base)
paul@0 494
                return
paul@0 495
            else:
paul@0 496
                bases.append(base_class)
paul@0 497
paul@0 498
        # Record bases for the class and retain the class name.
paul@107 499
        # Note that the function class does not inherit from the object class.
paul@0 500
paul@0 501
        class_name = self.get_object_path(n.name)
paul@0 502
paul@107 503
        if not bases and class_name != "__builtins__.core.object" and \
paul@107 504
                         class_name != "__builtins__.core.function":
paul@107 505
paul@0 506
            ref = self.get_object("__builtins__.object")
paul@0 507
            bases.append(ref)
paul@0 508
paul@0 509
        self.importer.classes[class_name] = self.classes[class_name] = bases
paul@0 510
        self.importer.subclasses[class_name] = set()
paul@0 511
        self.scope_globals[class_name] = set()
paul@0 512
paul@0 513
        # Set the definition before entering the namespace rather than
paul@0 514
        # afterwards because methods may reference it. In normal Python,
paul@0 515
        # a class is not accessible until the definition is complete, but
paul@0 516
        # methods can generally reference it since upon being called the
paul@0 517
        # class will already exist.
paul@0 518
paul@0 519
        self.set_definition(n.name, "<class>")
paul@0 520
paul@0 521
        in_class = self.in_class
paul@0 522
        self.in_class = class_name
paul@0 523
        self.set_instance_attr("__class__", Reference("<class>", class_name))
paul@0 524
        self.enter_namespace(n.name)
paul@107 525
paul@107 526
        # Do not provide the special instantiator attributes on the function
paul@107 527
        # class. Function instances provide these attributes.
paul@107 528
paul@107 529
        if class_name != "__builtins__.core.function":
paul@107 530
            self.set_name("__fn__") # special instantiator attribute
paul@107 531
            self.set_name("__args__") # special instantiator attribute
paul@107 532
paul@274 533
        self.set_name("__name__", self.get_constant("string", class_name).reference())
paul@274 534
paul@0 535
        self.process_structure_node(n.code)
paul@0 536
        self.exit_namespace()
paul@0 537
        self.in_class = in_class
paul@0 538
paul@0 539
    def process_from_node(self, n):
paul@0 540
paul@0 541
        "Process the given node 'n', importing from another module."
paul@0 542
paul@0 543
        path = self.get_namespace_path()
paul@0 544
paul@12 545
        module_name, names = self.get_module_name(n)
paul@12 546
        if module_name == self.name:
paul@12 547
            raise InspectError("Cannot import from the current module.", path, n)
paul@0 548
paul@18 549
        self.queue_module(module_name)
paul@0 550
paul@0 551
        # Attempt to obtain the referenced objects.
paul@0 552
paul@0 553
        for name, alias in n.names:
paul@0 554
            if name == "*":
paul@12 555
                raise InspectError("Only explicitly specified names can be imported from modules.", path, n)
paul@0 556
paul@0 557
            # Explicit names.
paul@0 558
paul@12 559
            ref = self.import_name_from_module(name, module_name)
paul@0 560
            value = ResolvedNameRef(alias or name, ref)
paul@0 561
            self.set_general_local(alias or name, value)
paul@0 562
paul@0 563
    def process_function_node(self, n, name):
paul@0 564
paul@0 565
        """
paul@0 566
        Process the given function or lambda node 'n' with the given 'name'.
paul@0 567
        """
paul@0 568
paul@0 569
        is_lambda = isinstance(n, compiler.ast.Lambda)
paul@0 570
paul@0 571
        # Where a function is declared conditionally, use a separate name for
paul@0 572
        # the definition, and assign the definition to the stated name.
paul@0 573
paul@0 574
        if (self.in_conditional or self.in_function) and not is_lambda:
paul@0 575
            original_name = name
paul@0 576
            name = self.get_lambda_name()
paul@0 577
        else:
paul@0 578
            original_name = None
paul@0 579
paul@0 580
        # Initialise argument and local records.
paul@0 581
paul@0 582
        function_name = self.get_object_path(name)
paul@46 583
        argnames = get_argnames(n.argnames)
paul@48 584
        is_method = self.in_class and not self.in_function
paul@0 585
paul@48 586
        # Remove explicit "self" from method parameters.
paul@46 587
paul@48 588
        if is_method and argnames and argnames[0] == "self":
paul@48 589
            del argnames[0]
paul@48 590
paul@48 591
        # Copy and propagate the parameters.
paul@46 592
paul@46 593
        self.importer.function_parameters[function_name] = \
paul@109 594
            self.function_parameters[function_name] = argnames[:]
paul@46 595
paul@46 596
        # Define all arguments/parameters in the local namespace.
paul@46 597
paul@109 598
        locals = \
paul@109 599
            self.importer.function_locals[function_name] = \
paul@109 600
            self.function_locals[function_name] = {}
paul@0 601
paul@48 602
        # Insert "self" into method locals.
paul@48 603
paul@48 604
        if is_method:
paul@48 605
            argnames.insert(0, "self")
paul@48 606
paul@47 607
        # Define "self" in terms of the class if in a method.
paul@47 608
        # This does not diminish the need for type-narrowing in the deducer.
paul@47 609
paul@47 610
        if argnames:
paul@48 611
            if self.in_class and not self.in_function and argnames[0] == "self":
paul@47 612
                locals[argnames[0]] = Reference("<instance>", self.in_class)
paul@47 613
            else:
paul@47 614
                locals[argnames[0]] = Reference("<var>")
paul@47 615
paul@47 616
        for argname in argnames[1:]:
paul@0 617
            locals[argname] = Reference("<var>")
paul@0 618
paul@0 619
        globals = self.scope_globals[function_name] = set()
paul@0 620
paul@0 621
        # Process the defaults.
paul@0 622
paul@0 623
        defaults = self.importer.function_defaults[function_name] = \
paul@0 624
                   self.function_defaults[function_name] = []
paul@0 625
paul@0 626
        for argname, default in compiler.ast.get_defaults(n):
paul@0 627
            if default:
paul@0 628
paul@0 629
                # Obtain any reference for the default.
paul@0 630
paul@0 631
                name_ref = self.process_structure_node(default)
paul@0 632
                defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>")))
paul@0 633
paul@0 634
        # Reset conditional tracking to focus on the function contents.
paul@0 635
paul@0 636
        in_conditional = self.in_conditional
paul@0 637
        self.in_conditional = False
paul@0 638
paul@0 639
        in_function = self.in_function
paul@0 640
        self.in_function = function_name
paul@0 641
paul@0 642
        self.enter_namespace(name)
paul@0 643
paul@251 644
        # Define a name attribute value for the function instance.
paul@251 645
paul@251 646
        ref = self.get_builtin_class("string")
paul@251 647
        self.reserve_constant(function_name, function_name, ref.get_origin())
paul@251 648
paul@0 649
        # Track attribute usage within the namespace.
paul@0 650
paul@0 651
        path = self.get_namespace_path()
paul@0 652
paul@0 653
        self.start_tracking(locals)
paul@0 654
        self.process_structure_node(n.code)
paul@0 655
        self.stop_tracking()
paul@0 656
paul@1 657
        # Exit to the parent.
paul@0 658
paul@0 659
        self.exit_namespace()
paul@0 660
paul@0 661
        # Update flags.
paul@0 662
paul@0 663
        self.in_function = in_function
paul@0 664
        self.in_conditional = in_conditional
paul@0 665
paul@0 666
        # Define the function using the appropriate name.
paul@0 667
paul@0 668
        self.set_definition(name, "<function>")
paul@0 669
paul@0 670
        # Where a function is set conditionally, assign the name.
paul@0 671
paul@0 672
        if original_name:
paul@322 673
            self.process_assignment_for_object(original_name, compiler.ast.Name(name))
paul@0 674
paul@0 675
    def process_global_node(self, n):
paul@0 676
paul@0 677
        """
paul@0 678
        Process the given "global" node 'n'.
paul@0 679
        """
paul@0 680
paul@0 681
        path = self.get_namespace_path()
paul@0 682
paul@0 683
        if path != self.name:
paul@0 684
            self.scope_globals[path].update(n.names)
paul@0 685
paul@0 686
    def process_if_node(self, n):
paul@0 687
paul@0 688
        """
paul@0 689
        Process the given "if" node 'n'.
paul@0 690
        """
paul@0 691
paul@0 692
        tracker = self.trackers[-1]
paul@0 693
        tracker.new_branchpoint()
paul@0 694
paul@0 695
        for test, body in n.tests:
paul@0 696
            self.process_structure_node(test)
paul@0 697
paul@0 698
            tracker.new_branch()
paul@0 699
paul@0 700
            in_conditional = self.in_conditional
paul@0 701
            self.in_conditional = True
paul@0 702
            self.process_structure_node(body)
paul@0 703
            self.in_conditional = in_conditional
paul@0 704
paul@0 705
            tracker.shelve_branch()
paul@0 706
paul@0 707
        # Maintain a branch for the else clause.
paul@0 708
paul@0 709
        tracker.new_branch()
paul@0 710
        if n.else_:
paul@0 711
            self.process_structure_node(n.else_)
paul@0 712
        tracker.shelve_branch()
paul@0 713
paul@0 714
        tracker.merge_branches()
paul@0 715
paul@0 716
    def process_import_node(self, n):
paul@0 717
paul@0 718
        "Process the given import node 'n'."
paul@0 719
paul@0 720
        path = self.get_namespace_path()
paul@0 721
paul@0 722
        # Load the mentioned module.
paul@0 723
paul@0 724
        for name, alias in n.names:
paul@12 725
            if name == self.name:
paul@12 726
                raise InspectError("Cannot import the current module.", path, n)
paul@0 727
paul@13 728
            self.set_module(alias or name.split(".")[-1], name)
paul@18 729
            self.queue_module(name, True)
paul@0 730
paul@0 731
    def process_invocation_node(self, n):
paul@0 732
paul@0 733
        "Process the given invocation node 'n'."
paul@0 734
paul@0 735
        path = self.get_namespace_path()
paul@0 736
paul@0 737
        self.allocate_arguments(path, n.args)
paul@0 738
paul@0 739
        try:
paul@107 740
            # Communicate to the invocation target expression that it forms the
paul@107 741
            # target of an invocation, potentially affecting attribute accesses.
paul@0 742
paul@88 743
            in_invocation = self.in_invocation
paul@88 744
            self.in_invocation = True
paul@107 745
paul@107 746
            # Process the expression, obtaining any identified reference.
paul@107 747
paul@0 748
            name_ref = self.process_structure_node(n.node)
paul@223 749
            self.in_invocation = False
paul@0 750
paul@0 751
            # Process the arguments.
paul@0 752
paul@0 753
            for arg in n.args:
paul@0 754
                self.process_structure_node(arg)
paul@0 755
paul@223 756
            self.in_invocation = in_invocation
paul@223 757
paul@0 758
            # Detect class invocations.
paul@0 759
paul@0 760
            if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"):
paul@0 761
                return InstanceRef(name_ref.reference().instance_of())
paul@0 762
paul@0 763
            elif isinstance(name_ref, NameRef):
paul@0 764
                return InvocationRef(name_ref)
paul@0 765
paul@226 766
            # Provide a general reference to indicate that something is produced
paul@226 767
            # by the invocation, useful for retaining assignment expression
paul@226 768
            # details.
paul@226 769
paul@226 770
            return VariableRef()
paul@0 771
paul@0 772
        finally:
paul@0 773
            self.deallocate_arguments(path, n.args)
paul@0 774
paul@0 775
    def process_lambda_node(self, n):
paul@0 776
paul@0 777
        "Process the given lambda node 'n'."
paul@0 778
paul@0 779
        name = self.get_lambda_name()
paul@0 780
        self.process_function_node(n, name)
paul@0 781
paul@0 782
        origin = self.get_object_path(name)
paul@210 783
paul@210 784
        if self.function_defaults.get(origin):
paul@210 785
            return None
paul@210 786
        else:
paul@210 787
            return ResolvedNameRef(name, Reference("<function>", origin))
paul@0 788
paul@0 789
    def process_logical_node(self, n):
paul@0 790
paul@0 791
        "Process the given operator node 'n'."
paul@0 792
paul@0 793
        self.process_operator_chain(n.nodes, self.process_structure_node)
paul@0 794
paul@0 795
    def process_name_node(self, n):
paul@0 796
paul@0 797
        "Process the given name node 'n'."
paul@0 798
paul@0 799
        path = self.get_namespace_path()
paul@0 800
paul@173 801
        # Special names that have already been identified.
paul@0 802
paul@0 803
        if n.name.startswith("$"):
paul@0 804
            value = self.get_special(n.name)
paul@0 805
            if value:
paul@0 806
                return value
paul@0 807
paul@0 808
        # Special case for operator functions introduced through code
paul@0 809
        # transformations.
paul@0 810
paul@0 811
        if n.name.startswith("$op"):
paul@0 812
paul@0 813
            # Obtain the location of the actual function defined in the operator
paul@0 814
            # package.
paul@0 815
paul@0 816
            op = n.name[len("$op"):]
paul@0 817
paul@0 818
            # Attempt to get a reference.
paul@0 819
paul@12 820
            ref = self.import_name_from_module(op, "operator")
paul@0 821
paul@0 822
            # Record the imported name and provide the resolved name reference.
paul@0 823
paul@0 824
            value = ResolvedNameRef(n.name, ref)
paul@0 825
            self.set_special(n.name, value)
paul@0 826
            return value
paul@0 827
paul@173 828
        # Special case for print operations.
paul@173 829
paul@173 830
        elif n.name.startswith("$print"):
paul@173 831
paul@173 832
            # Attempt to get a reference.
paul@173 833
paul@173 834
            ref = self.get_builtin("print_")
paul@173 835
paul@173 836
            # Record the imported name and provide the resolved name reference.
paul@173 837
paul@173 838
            value = ResolvedNameRef(n.name, ref)
paul@173 839
            self.set_special(n.name, value)
paul@173 840
            return value
paul@173 841
paul@60 842
        # Test for self usage, which is only allowed in methods.
paul@60 843
paul@60 844
        if n.name == "self" and not (self.in_function and self.in_class):
paul@60 845
            raise InspectError("Use of self is only allowed in methods.", path, n)
paul@60 846
paul@0 847
        # Record usage of the name.
paul@0 848
paul@0 849
        self.record_name(n.name)
paul@0 850
paul@0 851
        # Search for unknown names in non-function scopes immediately.
paul@0 852
        # External names in functions are resolved later.
paul@0 853
paul@0 854
        ref = self.find_name(n.name)
paul@0 855
        if ref:
paul@0 856
            return ResolvedNameRef(n.name, ref)
paul@0 857
paul@40 858
        # Explicitly-declared global names.
paul@0 859
paul@0 860
        elif self.in_function and n.name in self.scope_globals[path]:
paul@0 861
            return NameRef(n.name)
paul@0 862
paul@0 863
        # Examine other names.
paul@0 864
paul@0 865
        else:
paul@0 866
            tracker = self.trackers[-1]
paul@0 867
paul@0 868
            # Check local names.
paul@0 869
paul@0 870
            branches = tracker.tracking_name(n.name)
paul@0 871
paul@1 872
            # Local name.
paul@0 873
paul@0 874
            if branches:
paul@0 875
                self.record_branches_for_access(branches, n.name, None)
paul@117 876
                access_number = self.record_access_details(n.name, None, False, False)
paul@0 877
                return LocalNameRef(n.name, access_number)
paul@0 878
paul@40 879
            # Possible global or built-in name.
paul@0 880
paul@0 881
            else:
paul@0 882
                return NameRef(n.name)
paul@0 883
paul@0 884
    def process_operator_chain(self, nodes, fn):
paul@0 885
paul@0 886
        """
paul@0 887
        Process the given chain of 'nodes', applying 'fn' to each node or item.
paul@0 888
        Each node starts a new conditional region, effectively making a deeply-
paul@0 889
        nested collection of if-like statements.
paul@0 890
        """
paul@0 891
paul@0 892
        tracker = self.trackers[-1]
paul@0 893
paul@0 894
        for item in nodes:
paul@0 895
            tracker.new_branchpoint()
paul@0 896
            tracker.new_branch()
paul@0 897
            fn(item)
paul@0 898
paul@0 899
        for item in nodes[:-1]:
paul@0 900
            tracker.shelve_branch()
paul@0 901
            tracker.new_branch()
paul@0 902
            tracker.shelve_branch()
paul@0 903
            tracker.merge_branches()
paul@0 904
paul@0 905
        tracker.shelve_branch()
paul@0 906
        tracker.merge_branches()
paul@0 907
paul@0 908
    def process_try_node(self, n):
paul@0 909
paul@0 910
        """
paul@0 911
        Process the given "try...except" node 'n'.
paul@0 912
        """
paul@0 913
paul@0 914
        tracker = self.trackers[-1]
paul@0 915
        tracker.new_branchpoint()
paul@0 916
paul@0 917
        self.process_structure_node(n.body)
paul@0 918
paul@0 919
        for name, var, handler in n.handlers:
paul@0 920
            if name is not None:
paul@0 921
                self.process_structure_node(name)
paul@0 922
paul@0 923
            # Any abandoned branches from the body can now be resumed in a new
paul@0 924
            # branch.
paul@0 925
paul@0 926
            tracker.resume_abandoned_branches()
paul@0 927
paul@0 928
            # Establish the local for the handler.
paul@0 929
paul@0 930
            if var is not None:
paul@261 931
                self.process_assignment_node(var, None)
paul@0 932
            if handler is not None:
paul@0 933
                self.process_structure_node(handler)
paul@0 934
paul@0 935
            tracker.shelve_branch()
paul@0 936
paul@0 937
        # The else clause maintains the usage from the body but without the
paul@0 938
        # abandoned branches since they would never lead to the else clause
paul@0 939
        # being executed.
paul@0 940
paul@0 941
        if n.else_:
paul@0 942
            tracker.new_branch()
paul@0 943
            self.process_structure_node(n.else_)
paul@0 944
            tracker.shelve_branch()
paul@0 945
paul@0 946
        # Without an else clause, a null branch propagates the successful
paul@0 947
        # outcome.
paul@0 948
paul@0 949
        else:
paul@0 950
            tracker.new_branch()
paul@0 951
            tracker.shelve_branch()
paul@0 952
paul@0 953
        tracker.merge_branches()
paul@0 954
paul@0 955
    def process_try_finally_node(self, n):
paul@0 956
paul@0 957
        """
paul@0 958
        Process the given "try...finally" node 'n'.
paul@0 959
        """
paul@0 960
paul@0 961
        tracker = self.trackers[-1]
paul@0 962
        self.process_structure_node(n.body)
paul@0 963
paul@0 964
        # Any abandoned branches from the body can now be resumed.
paul@0 965
paul@0 966
        branches = tracker.resume_all_abandoned_branches()
paul@0 967
        self.process_structure_node(n.final)
paul@0 968
paul@0 969
        # At the end of the finally clause, abandoned branches are discarded.
paul@0 970
paul@0 971
        tracker.restore_active_branches(branches)
paul@0 972
paul@0 973
    def process_while_node(self, n):
paul@0 974
paul@0 975
        "Process the given while node 'n'."
paul@0 976
paul@0 977
        tracker = self.trackers[-1]
paul@0 978
        tracker.new_branchpoint(loop_node=True)
paul@0 979
paul@0 980
        # Evaluate any test or iterator outside the loop.
paul@0 981
paul@0 982
        self.process_structure_node(n.test)
paul@0 983
paul@0 984
        # Propagate attribute usage to branches.
paul@0 985
paul@0 986
        tracker.new_branch(loop_node=True)
paul@0 987
paul@0 988
        # Enter the loop.
paul@0 989
paul@0 990
        in_conditional = self.in_conditional
paul@0 991
        self.in_conditional = True
paul@0 992
        self.process_structure_node(n.body)
paul@0 993
        self.in_conditional = in_conditional
paul@0 994
paul@0 995
        # Continuing branches are resumed before any test.
paul@0 996
paul@0 997
        tracker.resume_continuing_branches()
paul@0 998
paul@0 999
        # Evaluate any continuation test within the body.
paul@0 1000
paul@0 1001
        self.process_structure_node(n.test)
paul@0 1002
paul@0 1003
        tracker.shelve_branch(loop_node=True)
paul@0 1004
paul@0 1005
        # Support the non-looping condition.
paul@0 1006
paul@0 1007
        tracker.new_branch()
paul@0 1008
        tracker.shelve_branch()
paul@0 1009
paul@0 1010
        tracker.merge_branches()
paul@0 1011
paul@0 1012
        # Evaluate any else clause outside branches.
paul@0 1013
paul@0 1014
        if n.else_:
paul@0 1015
            self.process_structure_node(n.else_)
paul@0 1016
paul@0 1017
        # Connect broken branches to the code after any loop.
paul@0 1018
paul@0 1019
        tracker.resume_broken_branches()
paul@0 1020
paul@0 1021
    # Branch tracking methods.
paul@0 1022
paul@0 1023
    def start_tracking(self, names):
paul@0 1024
paul@0 1025
        """
paul@0 1026
        Start tracking attribute usage for names in the current namespace,
paul@0 1027
        immediately registering the given 'names'.
paul@0 1028
        """
paul@0 1029
paul@0 1030
        path = self.get_namespace_path()
paul@0 1031
        parent = self.trackers[-1]
paul@0 1032
        tracker = BranchTracker()
paul@0 1033
        self.trackers.append(tracker)
paul@0 1034
paul@0 1035
        # Record the given names established as new branches.
paul@0 1036
paul@0 1037
        tracker.assign_names(names)
paul@0 1038
paul@0 1039
    def assign_name(self, name, name_ref):
paul@0 1040
paul@0 1041
        "Assign to 'name' the given 'name_ref' in the current namespace."
paul@0 1042
paul@0 1043
        name = self.get_name_for_tracking(name)
paul@0 1044
        self.trackers[-1].assign_names([name], [name_ref])
paul@0 1045
paul@0 1046
    def stop_tracking(self):
paul@0 1047
paul@0 1048
        """
paul@0 1049
        Stop tracking attribute usage, recording computed usage for the current
paul@0 1050
        namespace.
paul@0 1051
        """
paul@0 1052
paul@0 1053
        path = self.get_namespace_path()
paul@0 1054
        tracker = self.trackers.pop()
paul@0 1055
        self.record_assignments_for_access(tracker)
paul@0 1056
paul@0 1057
        self.attr_usage[path] = tracker.get_all_usage()
paul@0 1058
        self.name_initialisers[path] = tracker.get_all_values()
paul@0 1059
paul@0 1060
    def start_tracking_in_module(self):
paul@0 1061
paul@0 1062
        "Start tracking attribute usage in the module."
paul@0 1063
paul@0 1064
        tracker = BranchTracker()
paul@0 1065
        self.trackers.append(tracker)
paul@0 1066
paul@0 1067
    def stop_tracking_in_module(self):
paul@0 1068
paul@0 1069
        "Stop tracking attribute usage in the module."
paul@0 1070
paul@0 1071
        tracker = self.trackers[0]
paul@0 1072
        self.record_assignments_for_access(tracker)
paul@0 1073
        self.attr_usage[self.name] = tracker.get_all_usage()
paul@0 1074
        self.name_initialisers[self.name] = tracker.get_all_values()
paul@0 1075
paul@0 1076
    def record_assignments_for_access(self, tracker):
paul@0 1077
paul@0 1078
        """
paul@0 1079
        For the current path, use the given 'tracker' to record assignment
paul@0 1080
        version information for attribute accesses.
paul@0 1081
        """
paul@0 1082
paul@0 1083
        path = self.get_path_for_access()
paul@0 1084
paul@0 1085
        if not self.attr_accessor_branches.has_key(path):
paul@0 1086
            return
paul@0 1087
paul@0 1088
        init_item(self.attr_accessors, path, dict)
paul@0 1089
        attr_accessors = self.attr_accessors[path]
paul@0 1090
paul@0 1091
        # Obtain the branches applying during each access.
paul@0 1092
paul@0 1093
        for access, all_branches in self.attr_accessor_branches[path].items():
paul@0 1094
            name, attrnames = access
paul@0 1095
            init_item(attr_accessors, access, list)
paul@0 1096
paul@0 1097
            # Obtain the assignments applying to each branch.
paul@0 1098
paul@0 1099
            for branches in all_branches:
paul@0 1100
                positions = tracker.get_assignment_positions_for_branches(name, branches)
paul@0 1101
paul@0 1102
                # Detect missing name information.
paul@0 1103
paul@0 1104
                if None in positions:
paul@0 1105
                    globals = self.global_attr_accesses.get(path)
paul@0 1106
                    accesses = globals and globals.get(name)
paul@0 1107
                    if not accesses:
paul@0 1108
                        print >>sys.stderr, "In %s, %s may not be defined when used." % (
paul@0 1109
                            self.get_namespace_path(), name)
paul@0 1110
                    positions.remove(None)
paul@0 1111
paul@0 1112
                attr_accessors[access].append(positions)
paul@0 1113
paul@0 1114
    def record_branches_for_access(self, branches, name, attrnames):
paul@0 1115
paul@0 1116
        """
paul@0 1117
        Record the given 'branches' for an access involving the given 'name' and
paul@0 1118
        'attrnames'.
paul@0 1119
        """
paul@0 1120
paul@0 1121
        access = name, attrnames
paul@0 1122
        path = self.get_path_for_access()
paul@0 1123
paul@0 1124
        init_item(self.attr_accessor_branches, path, dict)
paul@0 1125
        attr_accessor_branches = self.attr_accessor_branches[path]
paul@0 1126
paul@0 1127
        init_item(attr_accessor_branches, access, list)
paul@0 1128
        attr_accessor_branches[access].append(branches)
paul@0 1129
paul@117 1130
    def record_access_details(self, name, attrnames, assignment, invocation):
paul@0 1131
paul@0 1132
        """
paul@0 1133
        For the given 'name' and 'attrnames', record an access indicating
paul@0 1134
        whether 'assignment' is occurring.
paul@0 1135
paul@0 1136
        These details correspond to accesses otherwise recorded by the attribute
paul@0 1137
        accessor and attribute access dictionaries.
paul@0 1138
        """
paul@0 1139
paul@0 1140
        access = name, attrnames
paul@0 1141
        path = self.get_path_for_access()
paul@0 1142
paul@0 1143
        init_item(self.attr_access_modifiers, path, dict)
paul@0 1144
        init_item(self.attr_access_modifiers[path], access, list)
paul@0 1145
paul@0 1146
        access_number = len(self.attr_access_modifiers[path][access])
paul@117 1147
        self.attr_access_modifiers[path][access].append((assignment, invocation))
paul@0 1148
        return access_number
paul@0 1149
paul@0 1150
    def record_global_access_details(self, name, attrnames):
paul@0 1151
paul@0 1152
        """
paul@0 1153
        Record details of a global access via the given 'name' involving the
paul@0 1154
        indicated 'attrnames'.
paul@0 1155
        """
paul@0 1156
paul@0 1157
        path = self.get_namespace_path()
paul@0 1158
paul@0 1159
        init_item(self.global_attr_accesses, path, dict)
paul@0 1160
        init_item(self.global_attr_accesses[path], name, set)
paul@0 1161
        self.global_attr_accesses[path][name].add(attrnames)
paul@0 1162
paul@0 1163
    # Namespace modification.
paul@0 1164
paul@0 1165
    def record_name(self, name):
paul@0 1166
paul@0 1167
        "Record the use of 'name' in a namespace."
paul@0 1168
paul@0 1169
        path = self.get_namespace_path()
paul@0 1170
        init_item(self.names_used, path, set)
paul@0 1171
        self.names_used[path].add(name)
paul@0 1172
paul@12 1173
    def set_module(self, name, module_name):
paul@0 1174
paul@0 1175
        """
paul@12 1176
        Set a module in the current namespace using the given 'name' associated
paul@12 1177
        with the corresponding 'module_name'.
paul@0 1178
        """
paul@0 1179
paul@0 1180
        if name:
paul@12 1181
            self.set_general_local(name, Reference("<module>", module_name))
paul@0 1182
paul@0 1183
    def set_definition(self, name, kind):
paul@0 1184
paul@0 1185
        """
paul@0 1186
        Set the definition having the given 'name' and 'kind'.
paul@0 1187
paul@0 1188
        Definitions are set in the static namespace hierarchy, but they can also
paul@0 1189
        be recorded for function locals.
paul@0 1190
        """
paul@0 1191
paul@0 1192
        if self.is_global(name):
paul@0 1193
            print >>sys.stderr, "In %s, %s is defined as being global." % (
paul@0 1194
                self.get_namespace_path(), name)
paul@0 1195
paul@0 1196
        path = self.get_object_path(name)
paul@0 1197
        self.set_object(path, kind)
paul@0 1198
paul@0 1199
        ref = self.get_object(path)
paul@0 1200
        if ref.get_kind() == "<var>":
paul@0 1201
            print >>sys.stderr, "In %s, %s is defined more than once." % (
paul@0 1202
                self.get_namespace_path(), name)
paul@0 1203
paul@0 1204
        if not self.is_global(name) and self.in_function:
paul@0 1205
            self.set_function_local(name, ref)
paul@0 1206
paul@0 1207
    def set_function_local(self, name, ref=None):
paul@0 1208
paul@0 1209
        "Set the local with the given 'name' and optional 'ref'."
paul@0 1210
paul@0 1211
        locals = self.function_locals[self.get_namespace_path()]
paul@0 1212
        multiple = not ref or locals.has_key(name) and locals[name] != ref
paul@0 1213
        locals[name] = multiple and Reference("<var>") or ref
paul@0 1214
paul@0 1215
    def assign_general_local(self, name, name_ref):
paul@0 1216
paul@0 1217
        """
paul@0 1218
        Set for 'name' the given 'name_ref', recording the name for attribute
paul@0 1219
        usage tracking.
paul@0 1220
        """
paul@0 1221
paul@0 1222
        self.set_general_local(name, name_ref)
paul@0 1223
        self.assign_name(name, name_ref)
paul@0 1224
paul@0 1225
    def set_general_local(self, name, value=None):
paul@0 1226
paul@0 1227
        """
paul@0 1228
        Set the 'name' with optional 'value' in any kind of local namespace,
paul@0 1229
        where the 'value' should be a reference if specified.
paul@0 1230
        """
paul@0 1231
paul@0 1232
        init_value = self.get_initialising_value(value)
paul@0 1233
paul@0 1234
        # Module global names.
paul@0 1235
paul@0 1236
        if self.is_global(name):
paul@0 1237
            path = self.get_global_path(name)
paul@0 1238
            self.set_object(path, init_value)
paul@0 1239
paul@0 1240
        # Function local names.
paul@0 1241
paul@0 1242
        elif self.in_function:
paul@0 1243
            path = self.get_object_path(name)
paul@0 1244
            self.set_function_local(name, init_value)
paul@0 1245
paul@0 1246
        # Other namespaces (classes).
paul@0 1247
paul@0 1248
        else:
paul@0 1249
            path = self.get_object_path(name)
paul@0 1250
            self.set_name(name, init_value)
paul@0 1251
paul@0 1252
    def set_name(self, name, ref=None):
paul@0 1253
paul@0 1254
        "Attach the 'name' with optional 'ref' to the current namespace."
paul@0 1255
paul@0 1256
        self.set_object(self.get_object_path(name), ref)
paul@0 1257
paul@0 1258
    def set_instance_attr(self, name, ref=None):
paul@0 1259
paul@0 1260
        """
paul@0 1261
        Add an instance attribute of the given 'name' to the current class,
paul@0 1262
        using the optional 'ref'.
paul@0 1263
        """
paul@0 1264
paul@251 1265
        self._set_instance_attr(self.in_class, name, ref)
paul@251 1266
paul@251 1267
    def _set_instance_attr(self, path, name, ref=None):
paul@251 1268
paul@251 1269
        init_item(self.instance_attrs, path, set)
paul@251 1270
        self.instance_attrs[path].add(name)
paul@0 1271
paul@0 1272
        if ref:
paul@251 1273
            init_item(self.instance_attr_constants, path, dict)
paul@251 1274
            self.instance_attr_constants[path][name] = ref
paul@0 1275
paul@0 1276
    def get_initialising_value(self, value):
paul@0 1277
paul@0 1278
        "Return a suitable initialiser reference for 'value'."
paul@0 1279
paul@25 1280
        # Includes LiteralSequenceRef, ResolvedNameRef...
paul@25 1281
paul@25 1282
        if isinstance(value, (NameRef, AccessRef, InstanceRef)):
paul@0 1283
            return value.reference()
paul@0 1284
paul@0 1285
        # In general, invocations do not produce known results. However, the
paul@0 1286
        # name initialisers are resolved once a module has been inspected.
paul@0 1287
paul@0 1288
        elif isinstance(value, InvocationRef):
paul@27 1289
            return value.reference()
paul@0 1290
paul@229 1291
        # Variable references are unknown results.
paul@229 1292
paul@229 1293
        elif isinstance(value, VariableRef):
paul@229 1294
            return value.reference()
paul@229 1295
paul@0 1296
        else:
paul@0 1297
            return value
paul@0 1298
paul@0 1299
    # Static, program-relative naming.
paul@0 1300
paul@0 1301
    def find_name(self, name):
paul@0 1302
paul@0 1303
        """
paul@0 1304
        Return the qualified name for the given 'name' used in the current
paul@0 1305
        non-function namespace.
paul@0 1306
        """
paul@0 1307
paul@0 1308
        path = self.get_namespace_path()
paul@0 1309
        ref = None
paul@0 1310
paul@0 1311
        if not self.in_function and name not in predefined_constants:
paul@0 1312
            if self.in_class:
paul@152 1313
                ref = self.get_object(self.get_object_path(name), False)
paul@0 1314
            if not ref:
paul@0 1315
                ref = self.get_global_or_builtin(name)
paul@0 1316
paul@0 1317
        return ref
paul@0 1318
paul@0 1319
    def get_class(self, node):
paul@0 1320
paul@0 1321
        """
paul@0 1322
        Use the given 'node' to obtain the identity of a class. Return a
paul@0 1323
        reference for the class. Unresolved dependencies are permitted and must
paul@0 1324
        be resolved later.
paul@0 1325
        """
paul@0 1326
paul@0 1327
        ref = self._get_class(node)
paul@0 1328
        return ref.has_kind(["<class>", "<depends>"]) and ref or None
paul@0 1329
paul@0 1330
    def _get_class(self, node):
paul@0 1331
paul@0 1332
        """
paul@0 1333
        Use the given 'node' to find a class definition. Return a reference to
paul@0 1334
        the class.
paul@0 1335
        """
paul@0 1336
paul@0 1337
        if isinstance(node, compiler.ast.Getattr):
paul@0 1338
paul@0 1339
            # Obtain the identity of the access target.
paul@0 1340
paul@0 1341
            ref = self._get_class(node.expr)
paul@0 1342
paul@0 1343
            # Where the target is a class or module, obtain the identity of the
paul@0 1344
            # attribute.
paul@0 1345
paul@0 1346
            if ref.has_kind(["<function>", "<var>"]):
paul@0 1347
                return None
paul@0 1348
            else:
paul@0 1349
                attrname = "%s.%s" % (ref.get_origin(), node.attrname)
paul@0 1350
                return self.get_object(attrname)
paul@0 1351
paul@0 1352
        # Names can be module-level or built-in.
paul@0 1353
paul@0 1354
        elif isinstance(node, compiler.ast.Name):
paul@0 1355
paul@0 1356
            # Record usage of the name and attempt to identify it.
paul@0 1357
paul@0 1358
            self.record_name(node.name)
paul@73 1359
            return self.find_name(node.name)
paul@0 1360
        else:
paul@0 1361
            return None
paul@0 1362
paul@0 1363
    def get_constant(self, name, value):
paul@0 1364
paul@0 1365
        "Return a constant reference for the given type 'name' and 'value'."
paul@0 1366
paul@12 1367
        ref = self.get_builtin_class(name)
paul@0 1368
        return self.get_constant_reference(ref, value)
paul@0 1369
paul@0 1370
    def get_literal_instance(self, n, name):
paul@0 1371
paul@0 1372
        "For node 'n', return a reference to an instance of 'name'."
paul@0 1373
paul@12 1374
        # Get a reference to the built-in class.
paul@0 1375
paul@12 1376
        ref = self.get_builtin_class(name)
paul@0 1377
paul@0 1378
        # Obtain the details of the literal itself.
paul@0 1379
        # An alias to the type is generated for sequences.
paul@0 1380
paul@0 1381
        if name in ("dict", "list", "tuple"):
paul@0 1382
            self.set_special_literal(name, ref)
paul@0 1383
            return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef)
paul@0 1384
paul@0 1385
        # Constant values are independently recorded.
paul@0 1386
paul@0 1387
        else:
paul@0 1388
            return self.get_constant_reference(ref, n.value)
paul@0 1389
paul@17 1390
    # Special names.
paul@0 1391
paul@17 1392
    def get_special(self, name):
paul@0 1393
paul@17 1394
        "Return any stored value for the given special 'name'."
paul@0 1395
paul@17 1396
        return self.special.get(name)
paul@17 1397
paul@17 1398
    def set_special(self, name, value):
paul@0 1399
paul@17 1400
        """
paul@17 1401
        Set a special 'name' that merely tracks the use of an implicit object
paul@17 1402
        'value'.
paul@17 1403
        """
paul@0 1404
paul@17 1405
        self.special[name] = value
paul@17 1406
paul@17 1407
    def set_special_literal(self, name, ref):
paul@0 1408
paul@17 1409
        """
paul@17 1410
        Set a special name for the literal type 'name' having type 'ref'. Such
paul@17 1411
        special names provide a way of referring to literal object types.
paul@17 1412
        """
paul@0 1413
paul@17 1414
        literal_name = "$L%s" % name
paul@17 1415
        value = ResolvedNameRef(literal_name, ref)
paul@17 1416
        self.set_special(literal_name, value)
paul@0 1417
paul@0 1418
    # Functions and invocations.
paul@0 1419
paul@36 1420
    def set_invocation_usage(self):
paul@36 1421
paul@36 1422
        """
paul@36 1423
        Discard the current invocation storage figures, retaining the maximum
paul@36 1424
        values.
paul@36 1425
        """
paul@36 1426
paul@36 1427
        for path, (current, maximum) in self.function_targets.items():
paul@36 1428
            self.importer.function_targets[path] = self.function_targets[path] = maximum
paul@36 1429
paul@36 1430
        for path, (current, maximum) in self.function_arguments.items():
paul@36 1431
            self.importer.function_arguments[path] = self.function_arguments[path] = maximum
paul@36 1432
paul@0 1433
    def allocate_arguments(self, path, args):
paul@0 1434
paul@0 1435
        """
paul@0 1436
        Allocate temporary argument storage using current and maximum
paul@0 1437
        requirements for the given 'path' and 'args'.
paul@0 1438
        """
paul@0 1439
paul@192 1440
        # Class and module initialisation is ultimately combined.
paul@192 1441
paul@192 1442
        if not self.in_function:
paul@192 1443
            path = self.name
paul@192 1444
paul@0 1445
        init_item(self.function_targets, path, lambda: [0, 0])
paul@0 1446
        t = self.function_targets[path]
paul@0 1447
        t[0] += 1
paul@0 1448
        t[1] = max(t[0], t[1])
paul@0 1449
paul@0 1450
        init_item(self.function_arguments, path, lambda: [0, 0])
paul@0 1451
        t = self.function_arguments[path]
paul@0 1452
        t[0] += len(args) + 1
paul@0 1453
        t[1] = max(t[0], t[1])
paul@0 1454
paul@0 1455
    def deallocate_arguments(self, path, args):
paul@0 1456
paul@0 1457
        "Deallocate temporary argument storage for the given 'path' and 'args'."
paul@0 1458
paul@192 1459
        # Class and module initialisation is ultimately combined.
paul@192 1460
paul@192 1461
        if not self.in_function:
paul@192 1462
            path = self.name
paul@192 1463
paul@0 1464
        self.function_targets[path][0] -= 1
paul@0 1465
        self.function_arguments[path][0] -= len(args) + 1
paul@0 1466
paul@0 1467
# vim: tabstop=4 expandtab shiftwidth=4