Lichen

Annotated inspector.py

118:900d641f42d6
2016-10-20 Paul Boddie Added some more support for generating invocation code, distinguishing between static invocation targets that are identified and whose functions can be obtained directly and other kinds of targets whose functions must be obtained via the special attribute.
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@12 30
                    LocalNameRef, NameRef, ResolvedNameRef
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@107 44
        self.in_assignment = False
paul@0 45
        self.in_class = False
paul@0 46
        self.in_conditional = False
paul@88 47
        self.in_invocation = False
paul@110 48
paul@110 49
        # Attribute chain state management.
paul@110 50
paul@110 51
        self.chain_assignment = []
paul@110 52
        self.chain_invocation = []
paul@110 53
paul@110 54
        # Accesses to global attributes.
paul@110 55
paul@0 56
        self.global_attr_accesses = {}
paul@0 57
paul@0 58
        # Usage tracking.
paul@0 59
paul@0 60
        self.trackers = []
paul@0 61
        self.attr_accessor_branches = {}
paul@0 62
paul@0 63
    def __repr__(self):
paul@0 64
        return "InspectedModule(%r, %r)" % (self.name, self.importer)
paul@0 65
paul@27 66
    # Principal methods.
paul@27 67
paul@0 68
    def parse(self, filename):
paul@0 69
paul@0 70
        "Parse the file having the given 'filename'."
paul@0 71
paul@0 72
        self.parse_file(filename)
paul@0 73
paul@0 74
        # Inspect the module.
paul@0 75
paul@0 76
        self.start_tracking_in_module()
paul@0 77
paul@0 78
        # Detect and record imports and globals declared in the module.
paul@0 79
paul@0 80
        self.assign_general_local("__name__", self.get_constant("str", self.name))
paul@0 81
        self.assign_general_local("__file__", self.get_constant("str", filename))
paul@0 82
        self.process_structure(self.astnode)
paul@0 83
paul@0 84
        # Set the class of the module after the definition has occurred.
paul@0 85
paul@0 86
        ref = self.get_builtin("object")
paul@0 87
        self.set_name("__class__", ref)
paul@0 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
        # Define the invocation requirements in each namespace.
paul@0 106
paul@12 107
        self.set_invocation_usage()
paul@0 108
paul@12 109
        # Propagate to the importer information needed in subsequent activities.
paul@0 110
paul@12 111
        self.propagate()
paul@0 112
paul@27 113
    # Accessory methods.
paul@0 114
paul@27 115
    def collect_names(self):
paul@0 116
paul@27 117
        "Collect the names used by each scope."
paul@0 118
paul@0 119
        for path in self.names_used.keys():
paul@27 120
            self.collect_names_for_path(path)
paul@27 121
paul@27 122
    def collect_names_for_path(self, path):
paul@0 123
paul@33 124
        """
paul@33 125
        Collect the names used by the given 'path'. These are propagated to the
paul@33 126
        importer in advance of any dependency resolution.
paul@33 127
        """
paul@0 128
paul@0 129
        names = self.names_used.get(path)
paul@0 130
        if not names:
paul@0 131
            return
paul@0 132
paul@0 133
        in_function = self.function_locals.has_key(path)
paul@0 134
paul@0 135
        for name in names:
paul@0 136
            if name in predefined_constants or in_function and name in self.function_locals[path]:
paul@0 137
                continue
paul@0 138
paul@35 139
            # Find local definitions (within dynamic namespaces).
paul@0 140
paul@0 141
            key = "%s.%s" % (path, name)
paul@27 142
            ref = self.get_resolved_object(key)
paul@0 143
            if ref:
paul@40 144
                self.set_name_reference(key, ref)
paul@0 145
                continue
paul@0 146
paul@40 147
            # Find global or known built-in definitions.
paul@0 148
paul@27 149
            ref = self.get_resolved_global_or_builtin(name)
paul@27 150
            if ref:
paul@40 151
                self.set_name_reference(key, ref)
paul@0 152
                continue
paul@0 153
paul@40 154
            # Find presumed built-in definitions.
paul@0 155
paul@40 156
            ref = self.get_builtin(name)
paul@40 157
            self.set_name_reference(key, ref)
paul@0 158
paul@40 159
    def set_name_reference(self, path, ref):
paul@0 160
paul@40 161
        "Map the given name 'path' to 'ref'."
paul@0 162
paul@40 163
        self.importer.all_name_references[path] = self.name_references[path] = ref
paul@0 164
paul@27 165
    def get_resolved_global_or_builtin(self, name):
paul@0 166
paul@27 167
        "Return the resolved global or built-in object with the given 'name'."
paul@0 168
paul@40 169
        # In some circumstances, the name is neither global nor recognised by
paul@40 170
        # the importer. It is then assumed to be a general built-in.
paul@0 171
paul@40 172
        return self.get_global(name) or \
paul@40 173
               self.importer.get_object("__builtins__.%s" % name)
paul@0 174
paul@0 175
    # Module structure traversal.
paul@0 176
paul@0 177
    def process_structure_node(self, n):
paul@0 178
paul@0 179
        "Process the individual node 'n'."
paul@0 180
paul@0 181
        # Module global detection.
paul@0 182
paul@0 183
        if isinstance(n, compiler.ast.Global):
paul@0 184
            self.process_global_node(n)
paul@0 185
paul@0 186
        # Module import declarations.
paul@0 187
paul@0 188
        elif isinstance(n, compiler.ast.From):
paul@0 189
            self.process_from_node(n)
paul@0 190
paul@0 191
        elif isinstance(n, compiler.ast.Import):
paul@0 192
            self.process_import_node(n)
paul@0 193
paul@0 194
        # Nodes using operator module functions.
paul@0 195
paul@0 196
        elif isinstance(n, compiler.ast.Operator):
paul@0 197
            return self.process_operator_node(n)
paul@0 198
paul@0 199
        elif isinstance(n, compiler.ast.AugAssign):
paul@0 200
            self.process_augassign_node(n)
paul@0 201
paul@0 202
        elif isinstance(n, compiler.ast.Compare):
paul@0 203
            return self.process_compare_node(n)
paul@0 204
paul@0 205
        elif isinstance(n, compiler.ast.Slice):
paul@0 206
            return self.process_slice_node(n)
paul@0 207
paul@0 208
        elif isinstance(n, compiler.ast.Sliceobj):
paul@0 209
            return self.process_sliceobj_node(n)
paul@0 210
paul@0 211
        elif isinstance(n, compiler.ast.Subscript):
paul@0 212
            return self.process_subscript_node(n)
paul@0 213
paul@0 214
        # Namespaces within modules.
paul@0 215
paul@0 216
        elif isinstance(n, compiler.ast.Class):
paul@0 217
            self.process_class_node(n)
paul@0 218
paul@0 219
        elif isinstance(n, compiler.ast.Function):
paul@0 220
            self.process_function_node(n, n.name)
paul@0 221
paul@0 222
        elif isinstance(n, compiler.ast.Lambda):
paul@0 223
            return self.process_lambda_node(n)
paul@0 224
paul@0 225
        # Assignments.
paul@0 226
paul@0 227
        elif isinstance(n, compiler.ast.Assign):
paul@0 228
paul@0 229
            # Handle each assignment node.
paul@0 230
paul@0 231
            for node in n.nodes:
paul@0 232
                self.process_assignment_node(node, n.expr)
paul@0 233
paul@0 234
        # Assignments within non-Assign nodes.
paul@0 235
paul@0 236
        elif isinstance(n, compiler.ast.AssName):
paul@0 237
            self.process_assignment_node(n, None)
paul@0 238
paul@0 239
        elif isinstance(n, compiler.ast.AssAttr):
paul@0 240
            self.process_attribute_access(n)
paul@0 241
paul@0 242
        # Accesses.
paul@0 243
paul@0 244
        elif isinstance(n, compiler.ast.Getattr):
paul@0 245
            return self.process_attribute_access(n)
paul@0 246
paul@0 247
        # Name recording for later testing.
paul@0 248
paul@0 249
        elif isinstance(n, compiler.ast.Name):
paul@0 250
            return self.process_name_node(n)
paul@0 251
paul@0 252
        # Conditional statement tracking.
paul@0 253
paul@0 254
        elif isinstance(n, compiler.ast.For):
paul@0 255
            self.process_for_node(n)
paul@0 256
paul@0 257
        elif isinstance(n, compiler.ast.While):
paul@0 258
            self.process_while_node(n)
paul@0 259
paul@0 260
        elif isinstance(n, compiler.ast.If):
paul@0 261
            self.process_if_node(n)
paul@0 262
paul@0 263
        elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
paul@0 264
            return self.process_logical_node(n)
paul@0 265
paul@0 266
        # Exception control-flow tracking.
paul@0 267
paul@0 268
        elif isinstance(n, compiler.ast.TryExcept):
paul@0 269
            self.process_try_node(n)
paul@0 270
paul@0 271
        elif isinstance(n, compiler.ast.TryFinally):
paul@0 272
            self.process_try_finally_node(n)
paul@0 273
paul@0 274
        # Control-flow modification statements.
paul@0 275
paul@0 276
        elif isinstance(n, compiler.ast.Break):
paul@0 277
            self.trackers[-1].suspend_broken_branch()
paul@0 278
paul@0 279
        elif isinstance(n, compiler.ast.Continue):
paul@0 280
            self.trackers[-1].suspend_continuing_branch()
paul@0 281
paul@0 282
        elif isinstance(n, compiler.ast.Raise):
paul@0 283
            self.process_structure(n)
paul@0 284
            self.trackers[-1].abandon_branch()
paul@0 285
paul@0 286
        elif isinstance(n, compiler.ast.Return):
paul@0 287
            self.process_structure(n)
paul@0 288
            self.trackers[-1].abandon_returning_branch()
paul@0 289
paul@0 290
        # Invocations.
paul@0 291
paul@0 292
        elif isinstance(n, compiler.ast.CallFunc):
paul@0 293
            return self.process_invocation_node(n)
paul@0 294
paul@0 295
        # Constant usage.
paul@0 296
paul@0 297
        elif isinstance(n, compiler.ast.Const):
paul@0 298
            return self.get_literal_instance(n, n.value.__class__.__name__)
paul@0 299
paul@0 300
        elif isinstance(n, compiler.ast.Dict):
paul@0 301
            return self.get_literal_instance(n, "dict")
paul@0 302
paul@0 303
        elif isinstance(n, compiler.ast.List):
paul@0 304
            return self.get_literal_instance(n, "list")
paul@0 305
paul@0 306
        elif isinstance(n, compiler.ast.Tuple):
paul@0 307
            return self.get_literal_instance(n, "tuple")
paul@0 308
paul@3 309
        # Unsupported nodes.
paul@3 310
paul@3 311
        elif isinstance(n, compiler.ast.GenExpr):
paul@3 312
            raise InspectError("Generator expressions are not supported.", self.get_namespace_path(), n)
paul@3 313
paul@3 314
        elif isinstance(n, compiler.ast.IfExp):
paul@3 315
            raise InspectError("If-else expressions are not supported.", self.get_namespace_path(), n)
paul@0 316
paul@0 317
        elif isinstance(n, compiler.ast.ListComp):
paul@3 318
            raise InspectError("List comprehensions are not supported.", self.get_namespace_path(), n)
paul@0 319
paul@0 320
        # All other nodes are processed depth-first.
paul@0 321
paul@0 322
        else:
paul@0 323
            self.process_structure(n)
paul@0 324
paul@0 325
        # By default, no expression details are returned.
paul@0 326
paul@0 327
        return None
paul@0 328
paul@0 329
    # Specific node handling.
paul@0 330
paul@0 331
    def process_assignment_node(self, n, expr):
paul@0 332
paul@0 333
        "Process the individual node 'n' to be assigned the contents of 'expr'."
paul@0 334
paul@0 335
        # Names and attributes are assigned the entire expression.
paul@0 336
paul@0 337
        if isinstance(n, compiler.ast.AssName):
paul@61 338
            if n.name == "self":
paul@61 339
                raise InspectError("Redefinition of self is not allowed.", self.get_namespace_path(), n)
paul@0 340
paul@0 341
            name_ref = expr and self.process_structure_node(expr)
paul@0 342
paul@0 343
            # Name assignments populate either function namespaces or the
paul@0 344
            # general namespace hierarchy.
paul@0 345
paul@0 346
            self.assign_general_local(n.name, name_ref)
paul@0 347
paul@0 348
            # Record usage of the name.
paul@0 349
paul@0 350
            self.record_name(n.name)
paul@0 351
paul@0 352
        elif isinstance(n, compiler.ast.AssAttr):
paul@0 353
            if expr: self.process_structure_node(expr)
paul@107 354
paul@107 355
            in_assignment = self.in_assignment
paul@107 356
            self.in_assignment = True
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@0 533
        self.assign_general_local("__name__", self.get_constant("str", class_name))
paul@0 534
        self.process_structure_node(n.code)
paul@0 535
        self.exit_namespace()
paul@0 536
        self.in_class = in_class
paul@0 537
paul@0 538
    def process_from_node(self, n):
paul@0 539
paul@0 540
        "Process the given node 'n', importing from another module."
paul@0 541
paul@0 542
        path = self.get_namespace_path()
paul@0 543
paul@12 544
        module_name, names = self.get_module_name(n)
paul@12 545
        if module_name == self.name:
paul@12 546
            raise InspectError("Cannot import from the current module.", path, n)
paul@0 547
paul@18 548
        self.queue_module(module_name)
paul@0 549
paul@0 550
        # Attempt to obtain the referenced objects.
paul@0 551
paul@0 552
        for name, alias in n.names:
paul@0 553
            if name == "*":
paul@12 554
                raise InspectError("Only explicitly specified names can be imported from modules.", path, n)
paul@0 555
paul@0 556
            # Explicit names.
paul@0 557
paul@12 558
            ref = self.import_name_from_module(name, module_name)
paul@0 559
            value = ResolvedNameRef(alias or name, ref)
paul@0 560
            self.set_general_local(alias or name, value)
paul@0 561
paul@0 562
    def process_function_node(self, n, name):
paul@0 563
paul@0 564
        """
paul@0 565
        Process the given function or lambda node 'n' with the given 'name'.
paul@0 566
        """
paul@0 567
paul@0 568
        is_lambda = isinstance(n, compiler.ast.Lambda)
paul@0 569
paul@0 570
        # Where a function is declared conditionally, use a separate name for
paul@0 571
        # the definition, and assign the definition to the stated name.
paul@0 572
paul@0 573
        if (self.in_conditional or self.in_function) and not is_lambda:
paul@0 574
            original_name = name
paul@0 575
            name = self.get_lambda_name()
paul@0 576
        else:
paul@0 577
            original_name = None
paul@0 578
paul@0 579
        # Initialise argument and local records.
paul@0 580
paul@0 581
        function_name = self.get_object_path(name)
paul@46 582
        argnames = get_argnames(n.argnames)
paul@48 583
        is_method = self.in_class and not self.in_function
paul@0 584
paul@48 585
        # Remove explicit "self" from method parameters.
paul@46 586
paul@48 587
        if is_method and argnames and argnames[0] == "self":
paul@48 588
            del argnames[0]
paul@48 589
paul@48 590
        # Copy and propagate the parameters.
paul@46 591
paul@46 592
        self.importer.function_parameters[function_name] = \
paul@109 593
            self.function_parameters[function_name] = argnames[:]
paul@46 594
paul@46 595
        # Define all arguments/parameters in the local namespace.
paul@46 596
paul@109 597
        locals = \
paul@109 598
            self.importer.function_locals[function_name] = \
paul@109 599
            self.function_locals[function_name] = {}
paul@0 600
paul@48 601
        # Insert "self" into method locals.
paul@48 602
paul@48 603
        if is_method:
paul@48 604
            argnames.insert(0, "self")
paul@48 605
paul@47 606
        # Define "self" in terms of the class if in a method.
paul@47 607
        # This does not diminish the need for type-narrowing in the deducer.
paul@47 608
paul@47 609
        if argnames:
paul@48 610
            if self.in_class and not self.in_function and argnames[0] == "self":
paul@47 611
                locals[argnames[0]] = Reference("<instance>", self.in_class)
paul@47 612
            else:
paul@47 613
                locals[argnames[0]] = Reference("<var>")
paul@47 614
paul@47 615
        for argname in argnames[1:]:
paul@0 616
            locals[argname] = Reference("<var>")
paul@0 617
paul@0 618
        globals = self.scope_globals[function_name] = set()
paul@0 619
paul@0 620
        # Process the defaults.
paul@0 621
paul@0 622
        defaults = self.importer.function_defaults[function_name] = \
paul@0 623
                   self.function_defaults[function_name] = []
paul@0 624
paul@0 625
        for argname, default in compiler.ast.get_defaults(n):
paul@0 626
            if default:
paul@0 627
paul@0 628
                # Obtain any reference for the default.
paul@0 629
paul@0 630
                name_ref = self.process_structure_node(default)
paul@0 631
                defaults.append((argname, name_ref.is_name() and name_ref.reference() or Reference("<var>")))
paul@0 632
paul@0 633
        # Reset conditional tracking to focus on the function contents.
paul@0 634
paul@0 635
        in_conditional = self.in_conditional
paul@0 636
        self.in_conditional = False
paul@0 637
paul@0 638
        in_function = self.in_function
paul@0 639
        self.in_function = function_name
paul@0 640
paul@0 641
        self.enter_namespace(name)
paul@0 642
paul@0 643
        # Track attribute usage within the namespace.
paul@0 644
paul@0 645
        path = self.get_namespace_path()
paul@0 646
paul@0 647
        self.start_tracking(locals)
paul@0 648
        self.process_structure_node(n.code)
paul@0 649
        self.stop_tracking()
paul@0 650
paul@1 651
        # Exit to the parent.
paul@0 652
paul@0 653
        self.exit_namespace()
paul@0 654
paul@0 655
        # Update flags.
paul@0 656
paul@0 657
        self.in_function = in_function
paul@0 658
        self.in_conditional = in_conditional
paul@0 659
paul@0 660
        # Define the function using the appropriate name.
paul@0 661
paul@0 662
        self.set_definition(name, "<function>")
paul@0 663
paul@0 664
        # Where a function is set conditionally, assign the name.
paul@0 665
paul@0 666
        if original_name:
paul@0 667
            self.process_assignment_for_function(original_name, name)
paul@0 668
paul@0 669
    def process_global_node(self, n):
paul@0 670
paul@0 671
        """
paul@0 672
        Process the given "global" node 'n'.
paul@0 673
        """
paul@0 674
paul@0 675
        path = self.get_namespace_path()
paul@0 676
paul@0 677
        if path != self.name:
paul@0 678
            self.scope_globals[path].update(n.names)
paul@0 679
paul@0 680
    def process_if_node(self, n):
paul@0 681
paul@0 682
        """
paul@0 683
        Process the given "if" node 'n'.
paul@0 684
        """
paul@0 685
paul@0 686
        tracker = self.trackers[-1]
paul@0 687
        tracker.new_branchpoint()
paul@0 688
paul@0 689
        for test, body in n.tests:
paul@0 690
            self.process_structure_node(test)
paul@0 691
paul@0 692
            tracker.new_branch()
paul@0 693
paul@0 694
            in_conditional = self.in_conditional
paul@0 695
            self.in_conditional = True
paul@0 696
            self.process_structure_node(body)
paul@0 697
            self.in_conditional = in_conditional
paul@0 698
paul@0 699
            tracker.shelve_branch()
paul@0 700
paul@0 701
        # Maintain a branch for the else clause.
paul@0 702
paul@0 703
        tracker.new_branch()
paul@0 704
        if n.else_:
paul@0 705
            self.process_structure_node(n.else_)
paul@0 706
        tracker.shelve_branch()
paul@0 707
paul@0 708
        tracker.merge_branches()
paul@0 709
paul@0 710
    def process_import_node(self, n):
paul@0 711
paul@0 712
        "Process the given import node 'n'."
paul@0 713
paul@0 714
        path = self.get_namespace_path()
paul@0 715
paul@0 716
        # Load the mentioned module.
paul@0 717
paul@0 718
        for name, alias in n.names:
paul@12 719
            if name == self.name:
paul@12 720
                raise InspectError("Cannot import the current module.", path, n)
paul@0 721
paul@13 722
            self.set_module(alias or name.split(".")[-1], name)
paul@18 723
            self.queue_module(name, True)
paul@0 724
paul@0 725
    def process_invocation_node(self, n):
paul@0 726
paul@0 727
        "Process the given invocation node 'n'."
paul@0 728
paul@0 729
        path = self.get_namespace_path()
paul@0 730
paul@0 731
        self.allocate_arguments(path, n.args)
paul@0 732
paul@0 733
        try:
paul@107 734
            # Communicate to the invocation target expression that it forms the
paul@107 735
            # target of an invocation, potentially affecting attribute accesses.
paul@0 736
paul@88 737
            in_invocation = self.in_invocation
paul@88 738
            self.in_invocation = True
paul@107 739
paul@107 740
            # Process the expression, obtaining any identified reference.
paul@107 741
paul@0 742
            name_ref = self.process_structure_node(n.node)
paul@88 743
            self.in_invocation = in_invocation
paul@0 744
paul@0 745
            # Process the arguments.
paul@0 746
paul@0 747
            for arg in n.args:
paul@0 748
                self.process_structure_node(arg)
paul@0 749
paul@0 750
            # Detect class invocations.
paul@0 751
paul@0 752
            if isinstance(name_ref, ResolvedNameRef) and name_ref.has_kind("<class>"):
paul@0 753
                return InstanceRef(name_ref.reference().instance_of())
paul@0 754
paul@0 755
            elif isinstance(name_ref, NameRef):
paul@0 756
                return InvocationRef(name_ref)
paul@0 757
paul@0 758
            return None
paul@0 759
paul@0 760
        finally:
paul@0 761
            self.deallocate_arguments(path, n.args)
paul@0 762
paul@0 763
    def process_lambda_node(self, n):
paul@0 764
paul@0 765
        "Process the given lambda node 'n'."
paul@0 766
paul@0 767
        name = self.get_lambda_name()
paul@0 768
        self.process_function_node(n, name)
paul@0 769
paul@0 770
        origin = self.get_object_path(name)
paul@0 771
        return ResolvedNameRef(name, Reference("<function>", origin))
paul@0 772
paul@0 773
    def process_logical_node(self, n):
paul@0 774
paul@0 775
        "Process the given operator node 'n'."
paul@0 776
paul@0 777
        self.process_operator_chain(n.nodes, self.process_structure_node)
paul@0 778
paul@0 779
    def process_name_node(self, n):
paul@0 780
paul@0 781
        "Process the given name node 'n'."
paul@0 782
paul@0 783
        path = self.get_namespace_path()
paul@0 784
paul@0 785
        # Special names.
paul@0 786
paul@0 787
        if n.name.startswith("$"):
paul@0 788
            value = self.get_special(n.name)
paul@0 789
            if value:
paul@0 790
                return value
paul@0 791
paul@0 792
        # Special case for operator functions introduced through code
paul@0 793
        # transformations.
paul@0 794
paul@0 795
        if n.name.startswith("$op"):
paul@0 796
paul@0 797
            # Obtain the location of the actual function defined in the operator
paul@0 798
            # package.
paul@0 799
paul@0 800
            op = n.name[len("$op"):]
paul@0 801
paul@0 802
            # Attempt to get a reference.
paul@0 803
paul@12 804
            ref = self.import_name_from_module(op, "operator")
paul@35 805
            self.add_deferred(ref)
paul@0 806
paul@0 807
            # Record the imported name and provide the resolved name reference.
paul@0 808
paul@0 809
            value = ResolvedNameRef(n.name, ref)
paul@0 810
            self.set_special(n.name, value)
paul@0 811
            return value
paul@0 812
paul@60 813
        # Test for self usage, which is only allowed in methods.
paul@60 814
paul@60 815
        if n.name == "self" and not (self.in_function and self.in_class):
paul@60 816
            raise InspectError("Use of self is only allowed in methods.", path, n)
paul@60 817
paul@0 818
        # Record usage of the name.
paul@0 819
paul@0 820
        self.record_name(n.name)
paul@0 821
paul@0 822
        # Search for unknown names in non-function scopes immediately.
paul@0 823
        # External names in functions are resolved later.
paul@0 824
paul@0 825
        ref = self.find_name(n.name)
paul@0 826
        if ref:
paul@0 827
            return ResolvedNameRef(n.name, ref)
paul@0 828
paul@40 829
        # Explicitly-declared global names.
paul@0 830
paul@0 831
        elif self.in_function and n.name in self.scope_globals[path]:
paul@0 832
            return NameRef(n.name)
paul@0 833
paul@0 834
        # Examine other names.
paul@0 835
paul@0 836
        else:
paul@0 837
            tracker = self.trackers[-1]
paul@0 838
paul@0 839
            # Check local names.
paul@0 840
paul@0 841
            branches = tracker.tracking_name(n.name)
paul@0 842
paul@1 843
            # Local name.
paul@0 844
paul@0 845
            if branches:
paul@0 846
                self.record_branches_for_access(branches, n.name, None)
paul@117 847
                access_number = self.record_access_details(n.name, None, False, False)
paul@0 848
                return LocalNameRef(n.name, access_number)
paul@0 849
paul@40 850
            # Possible global or built-in name.
paul@0 851
paul@0 852
            else:
paul@0 853
                return NameRef(n.name)
paul@0 854
paul@0 855
    def process_operator_chain(self, nodes, fn):
paul@0 856
paul@0 857
        """
paul@0 858
        Process the given chain of 'nodes', applying 'fn' to each node or item.
paul@0 859
        Each node starts a new conditional region, effectively making a deeply-
paul@0 860
        nested collection of if-like statements.
paul@0 861
        """
paul@0 862
paul@0 863
        tracker = self.trackers[-1]
paul@0 864
paul@0 865
        for item in nodes:
paul@0 866
            tracker.new_branchpoint()
paul@0 867
            tracker.new_branch()
paul@0 868
            fn(item)
paul@0 869
paul@0 870
        for item in nodes[:-1]:
paul@0 871
            tracker.shelve_branch()
paul@0 872
            tracker.new_branch()
paul@0 873
            tracker.shelve_branch()
paul@0 874
            tracker.merge_branches()
paul@0 875
paul@0 876
        tracker.shelve_branch()
paul@0 877
        tracker.merge_branches()
paul@0 878
paul@0 879
    def process_try_node(self, n):
paul@0 880
paul@0 881
        """
paul@0 882
        Process the given "try...except" node 'n'.
paul@0 883
        """
paul@0 884
paul@0 885
        tracker = self.trackers[-1]
paul@0 886
        tracker.new_branchpoint()
paul@0 887
paul@0 888
        self.process_structure_node(n.body)
paul@0 889
paul@0 890
        for name, var, handler in n.handlers:
paul@0 891
            if name is not None:
paul@0 892
                self.process_structure_node(name)
paul@0 893
paul@0 894
            # Any abandoned branches from the body can now be resumed in a new
paul@0 895
            # branch.
paul@0 896
paul@0 897
            tracker.resume_abandoned_branches()
paul@0 898
paul@0 899
            # Establish the local for the handler.
paul@0 900
paul@0 901
            if var is not None:
paul@0 902
                self.process_structure_node(var)
paul@0 903
            if handler is not None:
paul@0 904
                self.process_structure_node(handler)
paul@0 905
paul@0 906
            tracker.shelve_branch()
paul@0 907
paul@0 908
        # The else clause maintains the usage from the body but without the
paul@0 909
        # abandoned branches since they would never lead to the else clause
paul@0 910
        # being executed.
paul@0 911
paul@0 912
        if n.else_:
paul@0 913
            tracker.new_branch()
paul@0 914
            self.process_structure_node(n.else_)
paul@0 915
            tracker.shelve_branch()
paul@0 916
paul@0 917
        # Without an else clause, a null branch propagates the successful
paul@0 918
        # outcome.
paul@0 919
paul@0 920
        else:
paul@0 921
            tracker.new_branch()
paul@0 922
            tracker.shelve_branch()
paul@0 923
paul@0 924
        tracker.merge_branches()
paul@0 925
paul@0 926
    def process_try_finally_node(self, n):
paul@0 927
paul@0 928
        """
paul@0 929
        Process the given "try...finally" node 'n'.
paul@0 930
        """
paul@0 931
paul@0 932
        tracker = self.trackers[-1]
paul@0 933
        self.process_structure_node(n.body)
paul@0 934
paul@0 935
        # Any abandoned branches from the body can now be resumed.
paul@0 936
paul@0 937
        branches = tracker.resume_all_abandoned_branches()
paul@0 938
        self.process_structure_node(n.final)
paul@0 939
paul@0 940
        # At the end of the finally clause, abandoned branches are discarded.
paul@0 941
paul@0 942
        tracker.restore_active_branches(branches)
paul@0 943
paul@0 944
    def process_while_node(self, n):
paul@0 945
paul@0 946
        "Process the given while node 'n'."
paul@0 947
paul@0 948
        tracker = self.trackers[-1]
paul@0 949
        tracker.new_branchpoint(loop_node=True)
paul@0 950
paul@0 951
        # Evaluate any test or iterator outside the loop.
paul@0 952
paul@0 953
        self.process_structure_node(n.test)
paul@0 954
paul@0 955
        # Propagate attribute usage to branches.
paul@0 956
paul@0 957
        tracker.new_branch(loop_node=True)
paul@0 958
paul@0 959
        # Enter the loop.
paul@0 960
paul@0 961
        in_conditional = self.in_conditional
paul@0 962
        self.in_conditional = True
paul@0 963
        self.process_structure_node(n.body)
paul@0 964
        self.in_conditional = in_conditional
paul@0 965
paul@0 966
        # Continuing branches are resumed before any test.
paul@0 967
paul@0 968
        tracker.resume_continuing_branches()
paul@0 969
paul@0 970
        # Evaluate any continuation test within the body.
paul@0 971
paul@0 972
        self.process_structure_node(n.test)
paul@0 973
paul@0 974
        tracker.shelve_branch(loop_node=True)
paul@0 975
paul@0 976
        # Support the non-looping condition.
paul@0 977
paul@0 978
        tracker.new_branch()
paul@0 979
        tracker.shelve_branch()
paul@0 980
paul@0 981
        tracker.merge_branches()
paul@0 982
paul@0 983
        # Evaluate any else clause outside branches.
paul@0 984
paul@0 985
        if n.else_:
paul@0 986
            self.process_structure_node(n.else_)
paul@0 987
paul@0 988
        # Connect broken branches to the code after any loop.
paul@0 989
paul@0 990
        tracker.resume_broken_branches()
paul@0 991
paul@110 992
    # Attribute chain handling.
paul@110 993
paul@110 994
    def reset_attribute_chain(self):
paul@110 995
paul@110 996
        "Reset the attribute chain for a subexpression of an attribute access."
paul@110 997
paul@110 998
        CommonModule.reset_attribute_chain(self)
paul@110 999
        self.chain_assignment.append(self.in_assignment)
paul@110 1000
        self.chain_invocation.append(self.in_invocation)
paul@110 1001
        self.in_assignment = False
paul@110 1002
        self.in_invocation = False
paul@110 1003
paul@110 1004
    def restore_attribute_chain(self, attrs):
paul@110 1005
paul@110 1006
        "Restore the attribute chain for an attribute access."
paul@110 1007
paul@110 1008
        CommonModule.restore_attribute_chain(self, attrs)
paul@110 1009
        self.in_assignment = self.chain_assignment.pop()
paul@110 1010
        self.in_invocation = self.chain_invocation.pop()
paul@110 1011
paul@0 1012
    # Branch tracking methods.
paul@0 1013
paul@0 1014
    def start_tracking(self, names):
paul@0 1015
paul@0 1016
        """
paul@0 1017
        Start tracking attribute usage for names in the current namespace,
paul@0 1018
        immediately registering the given 'names'.
paul@0 1019
        """
paul@0 1020
paul@0 1021
        path = self.get_namespace_path()
paul@0 1022
        parent = self.trackers[-1]
paul@0 1023
        tracker = BranchTracker()
paul@0 1024
        self.trackers.append(tracker)
paul@0 1025
paul@0 1026
        # Record the given names established as new branches.
paul@0 1027
paul@0 1028
        tracker.assign_names(names)
paul@0 1029
paul@0 1030
    def assign_name(self, name, name_ref):
paul@0 1031
paul@0 1032
        "Assign to 'name' the given 'name_ref' in the current namespace."
paul@0 1033
paul@0 1034
        name = self.get_name_for_tracking(name)
paul@0 1035
        self.trackers[-1].assign_names([name], [name_ref])
paul@0 1036
paul@0 1037
    def stop_tracking(self):
paul@0 1038
paul@0 1039
        """
paul@0 1040
        Stop tracking attribute usage, recording computed usage for the current
paul@0 1041
        namespace.
paul@0 1042
        """
paul@0 1043
paul@0 1044
        path = self.get_namespace_path()
paul@0 1045
        tracker = self.trackers.pop()
paul@0 1046
        self.record_assignments_for_access(tracker)
paul@0 1047
paul@0 1048
        self.attr_usage[path] = tracker.get_all_usage()
paul@0 1049
        self.name_initialisers[path] = tracker.get_all_values()
paul@0 1050
paul@0 1051
    def start_tracking_in_module(self):
paul@0 1052
paul@0 1053
        "Start tracking attribute usage in the module."
paul@0 1054
paul@0 1055
        tracker = BranchTracker()
paul@0 1056
        self.trackers.append(tracker)
paul@0 1057
paul@0 1058
    def stop_tracking_in_module(self):
paul@0 1059
paul@0 1060
        "Stop tracking attribute usage in the module."
paul@0 1061
paul@0 1062
        tracker = self.trackers[0]
paul@0 1063
        self.record_assignments_for_access(tracker)
paul@0 1064
        self.attr_usage[self.name] = tracker.get_all_usage()
paul@0 1065
        self.name_initialisers[self.name] = tracker.get_all_values()
paul@0 1066
paul@0 1067
    def record_assignments_for_access(self, tracker):
paul@0 1068
paul@0 1069
        """
paul@0 1070
        For the current path, use the given 'tracker' to record assignment
paul@0 1071
        version information for attribute accesses.
paul@0 1072
        """
paul@0 1073
paul@0 1074
        path = self.get_path_for_access()
paul@0 1075
paul@0 1076
        if not self.attr_accessor_branches.has_key(path):
paul@0 1077
            return
paul@0 1078
paul@0 1079
        init_item(self.attr_accessors, path, dict)
paul@0 1080
        attr_accessors = self.attr_accessors[path]
paul@0 1081
paul@0 1082
        # Obtain the branches applying during each access.
paul@0 1083
paul@0 1084
        for access, all_branches in self.attr_accessor_branches[path].items():
paul@0 1085
            name, attrnames = access
paul@0 1086
            init_item(attr_accessors, access, list)
paul@0 1087
paul@0 1088
            # Obtain the assignments applying to each branch.
paul@0 1089
paul@0 1090
            for branches in all_branches:
paul@0 1091
                positions = tracker.get_assignment_positions_for_branches(name, branches)
paul@0 1092
paul@0 1093
                # Detect missing name information.
paul@0 1094
paul@0 1095
                if None in positions:
paul@0 1096
                    globals = self.global_attr_accesses.get(path)
paul@0 1097
                    accesses = globals and globals.get(name)
paul@0 1098
                    if not accesses:
paul@0 1099
                        print >>sys.stderr, "In %s, %s may not be defined when used." % (
paul@0 1100
                            self.get_namespace_path(), name)
paul@0 1101
                    positions.remove(None)
paul@0 1102
paul@0 1103
                attr_accessors[access].append(positions)
paul@0 1104
paul@0 1105
    def record_branches_for_access(self, branches, name, attrnames):
paul@0 1106
paul@0 1107
        """
paul@0 1108
        Record the given 'branches' for an access involving the given 'name' and
paul@0 1109
        'attrnames'.
paul@0 1110
        """
paul@0 1111
paul@0 1112
        access = name, attrnames
paul@0 1113
        path = self.get_path_for_access()
paul@0 1114
paul@0 1115
        init_item(self.attr_accessor_branches, path, dict)
paul@0 1116
        attr_accessor_branches = self.attr_accessor_branches[path]
paul@0 1117
paul@0 1118
        init_item(attr_accessor_branches, access, list)
paul@0 1119
        attr_accessor_branches[access].append(branches)
paul@0 1120
paul@117 1121
    def record_access_details(self, name, attrnames, assignment, invocation):
paul@0 1122
paul@0 1123
        """
paul@0 1124
        For the given 'name' and 'attrnames', record an access indicating
paul@0 1125
        whether 'assignment' is occurring.
paul@0 1126
paul@0 1127
        These details correspond to accesses otherwise recorded by the attribute
paul@0 1128
        accessor and attribute access dictionaries.
paul@0 1129
        """
paul@0 1130
paul@0 1131
        access = name, attrnames
paul@0 1132
        path = self.get_path_for_access()
paul@0 1133
paul@0 1134
        init_item(self.attr_access_modifiers, path, dict)
paul@0 1135
        init_item(self.attr_access_modifiers[path], access, list)
paul@0 1136
paul@0 1137
        access_number = len(self.attr_access_modifiers[path][access])
paul@117 1138
        self.attr_access_modifiers[path][access].append((assignment, invocation))
paul@0 1139
        return access_number
paul@0 1140
paul@0 1141
    def record_global_access_details(self, name, attrnames):
paul@0 1142
paul@0 1143
        """
paul@0 1144
        Record details of a global access via the given 'name' involving the
paul@0 1145
        indicated 'attrnames'.
paul@0 1146
        """
paul@0 1147
paul@0 1148
        path = self.get_namespace_path()
paul@0 1149
paul@0 1150
        init_item(self.global_attr_accesses, path, dict)
paul@0 1151
        init_item(self.global_attr_accesses[path], name, set)
paul@0 1152
        self.global_attr_accesses[path][name].add(attrnames)
paul@0 1153
paul@0 1154
    # Namespace modification.
paul@0 1155
paul@0 1156
    def record_name(self, name):
paul@0 1157
paul@0 1158
        "Record the use of 'name' in a namespace."
paul@0 1159
paul@0 1160
        path = self.get_namespace_path()
paul@0 1161
        init_item(self.names_used, path, set)
paul@0 1162
        self.names_used[path].add(name)
paul@0 1163
paul@12 1164
    def set_module(self, name, module_name):
paul@0 1165
paul@0 1166
        """
paul@12 1167
        Set a module in the current namespace using the given 'name' associated
paul@12 1168
        with the corresponding 'module_name'.
paul@0 1169
        """
paul@0 1170
paul@0 1171
        if name:
paul@12 1172
            self.set_general_local(name, Reference("<module>", module_name))
paul@0 1173
paul@0 1174
    def set_definition(self, name, kind):
paul@0 1175
paul@0 1176
        """
paul@0 1177
        Set the definition having the given 'name' and 'kind'.
paul@0 1178
paul@0 1179
        Definitions are set in the static namespace hierarchy, but they can also
paul@0 1180
        be recorded for function locals.
paul@0 1181
        """
paul@0 1182
paul@0 1183
        if self.is_global(name):
paul@0 1184
            print >>sys.stderr, "In %s, %s is defined as being global." % (
paul@0 1185
                self.get_namespace_path(), name)
paul@0 1186
paul@0 1187
        path = self.get_object_path(name)
paul@0 1188
        self.set_object(path, kind)
paul@0 1189
paul@0 1190
        ref = self.get_object(path)
paul@0 1191
        if ref.get_kind() == "<var>":
paul@0 1192
            print >>sys.stderr, "In %s, %s is defined more than once." % (
paul@0 1193
                self.get_namespace_path(), name)
paul@0 1194
paul@0 1195
        if not self.is_global(name) and self.in_function:
paul@0 1196
            self.set_function_local(name, ref)
paul@0 1197
paul@0 1198
    def set_function_local(self, name, ref=None):
paul@0 1199
paul@0 1200
        "Set the local with the given 'name' and optional 'ref'."
paul@0 1201
paul@0 1202
        locals = self.function_locals[self.get_namespace_path()]
paul@0 1203
        multiple = not ref or locals.has_key(name) and locals[name] != ref
paul@0 1204
        locals[name] = multiple and Reference("<var>") or ref
paul@0 1205
paul@0 1206
    def assign_general_local(self, name, name_ref):
paul@0 1207
paul@0 1208
        """
paul@0 1209
        Set for 'name' the given 'name_ref', recording the name for attribute
paul@0 1210
        usage tracking.
paul@0 1211
        """
paul@0 1212
paul@0 1213
        self.set_general_local(name, name_ref)
paul@0 1214
        self.assign_name(name, name_ref)
paul@0 1215
paul@0 1216
    def set_general_local(self, name, value=None):
paul@0 1217
paul@0 1218
        """
paul@0 1219
        Set the 'name' with optional 'value' in any kind of local namespace,
paul@0 1220
        where the 'value' should be a reference if specified.
paul@0 1221
        """
paul@0 1222
paul@0 1223
        init_value = self.get_initialising_value(value)
paul@0 1224
paul@0 1225
        # Module global names.
paul@0 1226
paul@0 1227
        if self.is_global(name):
paul@0 1228
            path = self.get_global_path(name)
paul@0 1229
            self.set_object(path, init_value)
paul@0 1230
paul@0 1231
        # Function local names.
paul@0 1232
paul@0 1233
        elif self.in_function:
paul@0 1234
            path = self.get_object_path(name)
paul@0 1235
            self.set_function_local(name, init_value)
paul@0 1236
paul@0 1237
        # Other namespaces (classes).
paul@0 1238
paul@0 1239
        else:
paul@0 1240
            path = self.get_object_path(name)
paul@0 1241
            self.set_name(name, init_value)
paul@0 1242
paul@0 1243
    def set_name(self, name, ref=None):
paul@0 1244
paul@0 1245
        "Attach the 'name' with optional 'ref' to the current namespace."
paul@0 1246
paul@0 1247
        self.set_object(self.get_object_path(name), ref)
paul@0 1248
paul@0 1249
    def set_instance_attr(self, name, ref=None):
paul@0 1250
paul@0 1251
        """
paul@0 1252
        Add an instance attribute of the given 'name' to the current class,
paul@0 1253
        using the optional 'ref'.
paul@0 1254
        """
paul@0 1255
paul@0 1256
        init_item(self.instance_attrs, self.in_class, set)
paul@0 1257
        self.instance_attrs[self.in_class].add(name)
paul@0 1258
paul@0 1259
        if ref:
paul@0 1260
            init_item(self.instance_attr_constants, self.in_class, dict)
paul@0 1261
            self.instance_attr_constants[self.in_class][name] = ref
paul@0 1262
paul@0 1263
    def get_initialising_value(self, value):
paul@0 1264
paul@0 1265
        "Return a suitable initialiser reference for 'value'."
paul@0 1266
paul@25 1267
        # Includes LiteralSequenceRef, ResolvedNameRef...
paul@25 1268
paul@25 1269
        if isinstance(value, (NameRef, AccessRef, InstanceRef)):
paul@0 1270
            return value.reference()
paul@0 1271
paul@0 1272
        # In general, invocations do not produce known results. However, the
paul@0 1273
        # name initialisers are resolved once a module has been inspected.
paul@0 1274
paul@0 1275
        elif isinstance(value, InvocationRef):
paul@27 1276
            return value.reference()
paul@0 1277
paul@0 1278
        else:
paul@0 1279
            return value
paul@0 1280
paul@0 1281
    # Static, program-relative naming.
paul@0 1282
paul@0 1283
    def find_name(self, name):
paul@0 1284
paul@0 1285
        """
paul@0 1286
        Return the qualified name for the given 'name' used in the current
paul@0 1287
        non-function namespace.
paul@0 1288
        """
paul@0 1289
paul@0 1290
        path = self.get_namespace_path()
paul@0 1291
        ref = None
paul@0 1292
paul@0 1293
        if not self.in_function and name not in predefined_constants:
paul@0 1294
            if self.in_class:
paul@0 1295
                ref = self.get_object(self.get_object_path(name))
paul@0 1296
            if not ref:
paul@0 1297
                ref = self.get_global_or_builtin(name)
paul@0 1298
paul@0 1299
        return ref
paul@0 1300
paul@0 1301
    def get_class(self, node):
paul@0 1302
paul@0 1303
        """
paul@0 1304
        Use the given 'node' to obtain the identity of a class. Return a
paul@0 1305
        reference for the class. Unresolved dependencies are permitted and must
paul@0 1306
        be resolved later.
paul@0 1307
        """
paul@0 1308
paul@0 1309
        ref = self._get_class(node)
paul@0 1310
        return ref.has_kind(["<class>", "<depends>"]) and ref or None
paul@0 1311
paul@0 1312
    def _get_class(self, node):
paul@0 1313
paul@0 1314
        """
paul@0 1315
        Use the given 'node' to find a class definition. Return a reference to
paul@0 1316
        the class.
paul@0 1317
        """
paul@0 1318
paul@0 1319
        if isinstance(node, compiler.ast.Getattr):
paul@0 1320
paul@0 1321
            # Obtain the identity of the access target.
paul@0 1322
paul@0 1323
            ref = self._get_class(node.expr)
paul@0 1324
paul@0 1325
            # Where the target is a class or module, obtain the identity of the
paul@0 1326
            # attribute.
paul@0 1327
paul@0 1328
            if ref.has_kind(["<function>", "<var>"]):
paul@0 1329
                return None
paul@0 1330
            else:
paul@0 1331
                attrname = "%s.%s" % (ref.get_origin(), node.attrname)
paul@0 1332
                return self.get_object(attrname)
paul@0 1333
paul@0 1334
        # Names can be module-level or built-in.
paul@0 1335
paul@0 1336
        elif isinstance(node, compiler.ast.Name):
paul@0 1337
paul@0 1338
            # Record usage of the name and attempt to identify it.
paul@0 1339
paul@0 1340
            self.record_name(node.name)
paul@73 1341
            return self.find_name(node.name)
paul@0 1342
        else:
paul@0 1343
            return None
paul@0 1344
paul@0 1345
    def get_constant(self, name, value):
paul@0 1346
paul@0 1347
        "Return a constant reference for the given type 'name' and 'value'."
paul@0 1348
paul@12 1349
        ref = self.get_builtin_class(name)
paul@0 1350
        return self.get_constant_reference(ref, value)
paul@0 1351
paul@0 1352
    def get_literal_instance(self, n, name):
paul@0 1353
paul@0 1354
        "For node 'n', return a reference to an instance of 'name'."
paul@0 1355
paul@12 1356
        # Get a reference to the built-in class.
paul@0 1357
paul@12 1358
        ref = self.get_builtin_class(name)
paul@0 1359
paul@0 1360
        # Obtain the details of the literal itself.
paul@0 1361
        # An alias to the type is generated for sequences.
paul@0 1362
paul@0 1363
        if name in ("dict", "list", "tuple"):
paul@0 1364
            self.set_special_literal(name, ref)
paul@0 1365
            return self.process_literal_sequence_node(n, name, ref, LiteralSequenceRef)
paul@0 1366
paul@0 1367
        # Constant values are independently recorded.
paul@0 1368
paul@0 1369
        else:
paul@0 1370
            return self.get_constant_reference(ref, n.value)
paul@0 1371
paul@17 1372
    # Special names.
paul@0 1373
paul@17 1374
    def get_special(self, name):
paul@0 1375
paul@17 1376
        "Return any stored value for the given special 'name'."
paul@0 1377
paul@17 1378
        return self.special.get(name)
paul@17 1379
paul@17 1380
    def set_special(self, name, value):
paul@0 1381
paul@17 1382
        """
paul@17 1383
        Set a special 'name' that merely tracks the use of an implicit object
paul@17 1384
        'value'.
paul@17 1385
        """
paul@0 1386
paul@17 1387
        self.special[name] = value
paul@17 1388
paul@17 1389
    def set_special_literal(self, name, ref):
paul@0 1390
paul@17 1391
        """
paul@17 1392
        Set a special name for the literal type 'name' having type 'ref'. Such
paul@17 1393
        special names provide a way of referring to literal object types.
paul@17 1394
        """
paul@0 1395
paul@17 1396
        literal_name = "$L%s" % name
paul@17 1397
        value = ResolvedNameRef(literal_name, ref)
paul@17 1398
        self.set_special(literal_name, value)
paul@0 1399
paul@0 1400
    # Functions and invocations.
paul@0 1401
paul@36 1402
    def set_invocation_usage(self):
paul@36 1403
paul@36 1404
        """
paul@36 1405
        Discard the current invocation storage figures, retaining the maximum
paul@36 1406
        values.
paul@36 1407
        """
paul@36 1408
paul@36 1409
        for path, (current, maximum) in self.function_targets.items():
paul@36 1410
            self.importer.function_targets[path] = self.function_targets[path] = maximum
paul@36 1411
paul@36 1412
        for path, (current, maximum) in self.function_arguments.items():
paul@36 1413
            self.importer.function_arguments[path] = self.function_arguments[path] = maximum
paul@36 1414
paul@0 1415
    def allocate_arguments(self, path, args):
paul@0 1416
paul@0 1417
        """
paul@0 1418
        Allocate temporary argument storage using current and maximum
paul@0 1419
        requirements for the given 'path' and 'args'.
paul@0 1420
        """
paul@0 1421
paul@0 1422
        init_item(self.function_targets, path, lambda: [0, 0])
paul@0 1423
        t = self.function_targets[path]
paul@0 1424
        t[0] += 1
paul@0 1425
        t[1] = max(t[0], t[1])
paul@0 1426
paul@0 1427
        init_item(self.function_arguments, path, lambda: [0, 0])
paul@0 1428
        t = self.function_arguments[path]
paul@0 1429
        t[0] += len(args) + 1
paul@0 1430
        t[1] = max(t[0], t[1])
paul@0 1431
paul@0 1432
    def deallocate_arguments(self, path, args):
paul@0 1433
paul@0 1434
        "Deallocate temporary argument storage for the given 'path' and 'args'."
paul@0 1435
paul@0 1436
        self.function_targets[path][0] -= 1
paul@0 1437
        self.function_arguments[path][0] -= len(args) + 1
paul@0 1438
paul@0 1439
# vim: tabstop=4 expandtab shiftwidth=4