Lichen

Annotated inspector.py

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