Lichen

Annotated translator.py

1027:dd0745ab8b8a
5 months ago Paul Boddie Reordered GCC arguments to prevent linking failures. Someone decided to change the GCC invocation or linking semantics at some point, meaning that libraries specified "too early" in the argument list no longer provide the symbols required by the program objects, whereas specifying them at the end of the argument list allows those symbols to be found and obtained.
paul@113 1
#!/usr/bin/env python
paul@113 2
paul@113 3
"""
paul@113 4
Translate programs.
paul@113 5
paul@828 6
Copyright (C) 2015, 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
paul@113 7
paul@113 8
This program is free software; you can redistribute it and/or modify it under
paul@113 9
the terms of the GNU General Public License as published by the Free Software
paul@113 10
Foundation; either version 3 of the License, or (at your option) any later
paul@113 11
version.
paul@113 12
paul@113 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@113 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@113 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@113 16
details.
paul@113 17
paul@113 18
You should have received a copy of the GNU General Public License along with
paul@113 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@113 20
"""
paul@113 21
paul@791 22
from common import AccessLocation, CommonModule, CommonOutput, Location, \
paul@609 23
                   first, get_builtin_class, init_item, is_newer, \
paul@609 24
                   predefined_constants
paul@618 25
from encoders import encode_access_instruction, encode_access_instruction_arg, \
paul@636 26
                     encode_function_pointer, encode_literal_instantiator, \
paul@636 27
                     encode_instantiator_pointer, encode_path, encode_symbol, \
paul@828 28
                     encode_type_attribute, is_type_attribute, \
paul@828 29
                     type_ops, typename_ops
paul@545 30
from errors import InspectError, TranslateError
paul@113 31
from os.path import exists, join
paul@113 32
from os import makedirs
paul@749 33
from referencing import Reference, combine_types
paul@636 34
from results import Result
paul@636 35
from transresults import TrConstantValueRef, TrInstanceRef, \
paul@636 36
                         TrLiteralSequenceRef, TrResolvedNameRef, \
paul@685 37
                         AliasResult, AttrResult, Expression, InstantiationResult, \
paul@636 38
                         InvocationResult, LogicalOperationResult, \
paul@636 39
                         LogicalResult, NegationResult, PredefinedConstantRef, \
paul@636 40
                         ReturnRef
paul@482 41
from StringIO import StringIO
paul@113 42
import compiler
paul@556 43
import sys
paul@113 44
paul@113 45
class Translator(CommonOutput):
paul@113 46
paul@113 47
    "A program translator."
paul@113 48
paul@113 49
    def __init__(self, importer, deducer, optimiser, output):
paul@113 50
        self.importer = importer
paul@113 51
        self.deducer = deducer
paul@113 52
        self.optimiser = optimiser
paul@113 53
        self.output = output
paul@113 54
paul@633 55
    def to_output(self, reset=False, debug=False, gc_sections=False):
paul@609 56
paul@609 57
        "Write a program to the configured output directory."
paul@609 58
paul@609 59
        # Make a directory for the final sources.
paul@609 60
paul@113 61
        output = join(self.output, "src")
paul@113 62
paul@113 63
        if not exists(output):
paul@113 64
            makedirs(output)
paul@113 65
paul@609 66
        # Clean the output directory of irrelevant data.
paul@609 67
paul@617 68
        self.check_output("debug=%r gc_sections=%r" % (debug, gc_sections))
paul@113 69
paul@113 70
        for module in self.importer.modules.values():
paul@609 71
            output_filename = join(output, "%s.c" % module.name)
paul@609 72
paul@609 73
            # Do not generate modules in the native package. They are provided
paul@609 74
            # by native functionality source files.
paul@609 75
paul@354 76
            parts = module.name.split(".")
paul@609 77
paul@633 78
            if parts[0] != "native" and \
paul@633 79
               (reset or is_newer(module.filename, output_filename)):
paul@633 80
paul@161 81
                tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
paul@609 82
                tm.translate(module.filename, output_filename)
paul@113 83
paul@636 84
paul@629 85
paul@113 86
def make_expression(expr):
paul@113 87
paul@113 88
    "Make a new expression from the existing 'expr'."
paul@113 89
paul@636 90
    if isinstance(expr, Result):
paul@113 91
        return expr
paul@113 92
    else:
paul@113 93
        return Expression(str(expr))
paul@113 94
paul@552 95
paul@552 96
paul@113 97
# The actual translation process itself.
paul@113 98
paul@113 99
class TranslatedModule(CommonModule):
paul@113 100
paul@113 101
    "A module translator."
paul@113 102
paul@113 103
    def __init__(self, name, importer, deducer, optimiser):
paul@113 104
        CommonModule.__init__(self, name, importer)
paul@113 105
        self.deducer = deducer
paul@113 106
        self.optimiser = optimiser
paul@113 107
paul@113 108
        # Output stream.
paul@113 109
paul@482 110
        self.out_toplevel = self.out = None
paul@113 111
        self.indent = 0
paul@113 112
        self.tabstop = "    "
paul@113 113
paul@113 114
        # Recorded namespaces.
paul@113 115
paul@113 116
        self.namespaces = []
paul@113 117
        self.in_conditional = False
paul@858 118
        self.in_parameter_list = False
paul@113 119
paul@144 120
        # Exception raising adjustments.
paul@144 121
paul@144 122
        self.in_try_finally = False
paul@189 123
        self.in_try_except = False
paul@144 124
paul@237 125
        # Attribute access and accessor counting.
paul@113 126
paul@113 127
        self.attr_accesses = {}
paul@237 128
        self.attr_accessors = {}
paul@113 129
paul@482 130
        # Special variable usage.
paul@482 131
paul@482 132
        self.temp_usage = {}
paul@482 133
paul@593 134
        # Initialise some data used for attribute access generation.
paul@593 135
paul@593 136
        self.init_substitutions()
paul@593 137
paul@113 138
    def __repr__(self):
paul@113 139
        return "TranslatedModule(%r, %r)" % (self.name, self.importer)
paul@113 140
paul@113 141
    def translate(self, filename, output_filename):
paul@113 142
paul@113 143
        """
paul@113 144
        Parse the file having the given 'filename', writing the translation to
paul@113 145
        the given 'output_filename'.
paul@113 146
        """
paul@113 147
paul@113 148
        self.parse_file(filename)
paul@113 149
paul@113 150
        # Collect function namespaces for separate processing.
paul@113 151
paul@113 152
        self.record_namespaces(self.astnode)
paul@113 153
paul@113 154
        # Reset the lambda naming (in order to obtain the same names again) and
paul@113 155
        # translate the program.
paul@113 156
paul@113 157
        self.reset_lambdas()
paul@113 158
paul@482 159
        self.out_toplevel = self.out = open(output_filename, "w")
paul@113 160
        try:
paul@128 161
            self.start_output()
paul@128 162
paul@113 163
            # Process namespaces, writing the translation.
paul@113 164
paul@113 165
            for path, node in self.namespaces:
paul@113 166
                self.process_namespace(path, node)
paul@113 167
paul@113 168
            # Process the module namespace including class namespaces.
paul@113 169
paul@113 170
            self.process_namespace([], self.astnode)
paul@113 171
paul@113 172
        finally:
paul@113 173
            self.out.close()
paul@113 174
paul@113 175
    def have_object(self):
paul@113 176
paul@113 177
        "Return whether a namespace is a recorded object."
paul@113 178
paul@113 179
        return self.importer.objects.get(self.get_namespace_path())
paul@113 180
paul@156 181
    def get_builtin_class(self, name):
paul@156 182
paul@156 183
        "Return a reference to the actual object providing 'name'."
paul@156 184
paul@538 185
        return self.importer.get_object(get_builtin_class(name))
paul@156 186
paul@156 187
    def is_method(self, path):
paul@156 188
paul@156 189
        "Return whether 'path' is a method."
paul@156 190
paul@113 191
        class_name, method_name = path.rsplit(".", 1)
paul@267 192
        return self.importer.classes.has_key(class_name) and class_name or None
paul@113 193
paul@208 194
    def in_method(self):
paul@208 195
paul@208 196
        "Return whether the current namespace provides a method."
paul@208 197
paul@208 198
        return self.in_function and self.is_method(self.get_namespace_path())
paul@208 199
paul@113 200
    # Namespace recording.
paul@113 201
paul@113 202
    def record_namespaces(self, node):
paul@113 203
paul@113 204
        "Process the program structure 'node', recording namespaces."
paul@113 205
paul@113 206
        for n in node.getChildNodes():
paul@113 207
            self.record_namespaces_in_node(n)
paul@113 208
paul@113 209
    def record_namespaces_in_node(self, node):
paul@113 210
paul@113 211
        "Process the program structure 'node', recording namespaces."
paul@113 212
paul@113 213
        # Function namespaces within modules, classes and other functions.
paul@113 214
        # Functions appearing within conditional statements are given arbitrary
paul@113 215
        # names.
paul@113 216
paul@113 217
        if isinstance(node, compiler.ast.Function):
paul@113 218
            self.record_function_node(node, (self.in_conditional or self.in_function) and self.get_lambda_name() or node.name)
paul@113 219
paul@113 220
        elif isinstance(node, compiler.ast.Lambda):
paul@113 221
            self.record_function_node(node, self.get_lambda_name())
paul@113 222
paul@113 223
        # Classes are visited, but may be ignored if inside functions.
paul@113 224
paul@113 225
        elif isinstance(node, compiler.ast.Class):
paul@113 226
            self.enter_namespace(node.name)
paul@113 227
            if self.have_object():
paul@113 228
                self.record_namespaces(node)
paul@113 229
            self.exit_namespace()
paul@113 230
paul@113 231
        # Conditional nodes are tracked so that function definitions may be
paul@113 232
        # handled. Since "for" loops are converted to "while" loops, they are
paul@113 233
        # included here.
paul@113 234
paul@113 235
        elif isinstance(node, (compiler.ast.For, compiler.ast.If, compiler.ast.While)):
paul@113 236
            in_conditional = self.in_conditional
paul@113 237
            self.in_conditional = True
paul@113 238
            self.record_namespaces(node)
paul@113 239
            self.in_conditional = in_conditional
paul@113 240
paul@113 241
        # All other nodes are processed depth-first.
paul@113 242
paul@113 243
        else:
paul@113 244
            self.record_namespaces(node)
paul@113 245
paul@113 246
    def record_function_node(self, n, name):
paul@113 247
paul@113 248
        """
paul@113 249
        Record the given function, lambda, if expression or list comprehension
paul@113 250
        node 'n' with the given 'name'.
paul@113 251
        """
paul@113 252
paul@113 253
        self.in_function = True
paul@113 254
        self.enter_namespace(name)
paul@113 255
paul@113 256
        if self.have_object():
paul@113 257
paul@113 258
            # Record the namespace path and the node itself.
paul@113 259
paul@113 260
            self.namespaces.append((self.namespace_path[:], n))
paul@113 261
            self.record_namespaces_in_node(n.code)
paul@113 262
paul@113 263
        self.exit_namespace()
paul@113 264
        self.in_function = False
paul@113 265
paul@113 266
    # Constant referencing.
paul@113 267
paul@405 268
    def get_literal_instance(self, n, name=None):
paul@113 269
paul@113 270
        """
paul@405 271
        For node 'n', return a reference for the type of the given 'name', or if
paul@405 272
        'name' is not specified, deduce the type from the value.
paul@113 273
        """
paul@113 274
paul@366 275
        # Handle stray None constants (Sliceobj seems to produce them).
paul@366 276
paul@405 277
        if name is None and n.value is None:
paul@366 278
            return self.process_name_node(compiler.ast.Name("None"))
paul@366 279
paul@113 280
        if name in ("dict", "list", "tuple"):
paul@405 281
            ref = self.get_builtin_class(name)
paul@113 282
            return self.process_literal_sequence_node(n, name, ref, TrLiteralSequenceRef)
paul@113 283
        else:
paul@537 284
            value, typename, encoding = self.get_constant_value(n.value, n.literals)
paul@538 285
            ref = self.get_builtin_class(typename)
paul@397 286
            value_type = ref.get_origin()
paul@397 287
paul@113 288
            path = self.get_namespace_path()
paul@406 289
paul@406 290
            # Obtain the local numbering of the constant and thus the
paul@406 291
            # locally-qualified name.
paul@406 292
paul@406 293
            local_number = self.importer.all_constants[path][(value, value_type, encoding)]
paul@113 294
            constant_name = "$c%d" % local_number
paul@113 295
            objpath = self.get_object_path(constant_name)
paul@406 296
paul@406 297
            # Obtain the unique identifier for the constant.
paul@406 298
paul@113 299
            number = self.optimiser.constant_numbers[objpath]
paul@394 300
            return TrConstantValueRef(constant_name, ref.instance_of(), value, number)
paul@113 301
paul@113 302
    # Namespace translation.
paul@113 303
paul@113 304
    def process_namespace(self, path, node):
paul@113 305
paul@113 306
        """
paul@113 307
        Process the namespace for the given 'path' defined by the given 'node'.
paul@113 308
        """
paul@113 309
paul@113 310
        self.namespace_path = path
paul@113 311
paul@113 312
        if isinstance(node, (compiler.ast.Function, compiler.ast.Lambda)):
paul@113 313
            self.in_function = True
paul@113 314
            self.process_function_body_node(node)
paul@113 315
        else:
paul@113 316
            self.in_function = False
paul@192 317
            self.function_target = 0
paul@828 318
            self.max_function_target = 0
paul@828 319
            self.context_index = 0
paul@828 320
            self.max_context_index = 0
paul@858 321
            self.accessor_index = 0
paul@858 322
            self.max_accessor_index = 0
paul@113 323
            self.start_module()
paul@113 324
            self.process_structure(node)
paul@113 325
            self.end_module()
paul@113 326
paul@113 327
    def process_structure(self, node):
paul@113 328
paul@113 329
        "Process the given 'node' or result."
paul@113 330
paul@144 331
        # Handle processing requests on results.
paul@144 332
paul@636 333
        if isinstance(node, Result):
paul@113 334
            return node
paul@144 335
paul@144 336
        # Handle processing requests on nodes.
paul@144 337
paul@113 338
        else:
paul@144 339
            l = CommonModule.process_structure(self, node)
paul@144 340
paul@144 341
            # Return indications of return statement usage.
paul@144 342
paul@144 343
            if l and isinstance(l[-1], ReturnRef):
paul@144 344
                return l[-1]
paul@144 345
            else:
paul@144 346
                return None
paul@113 347
paul@113 348
    def process_structure_node(self, n):
paul@113 349
paul@113 350
        "Process the individual node 'n'."
paul@113 351
paul@113 352
        # Plain statements emit their expressions.
paul@113 353
paul@113 354
        if isinstance(n, compiler.ast.Discard):
paul@113 355
            expr = self.process_structure_node(n.expr)
paul@113 356
            self.statement(expr)
paul@113 357
paul@314 358
        # Module import declarations.
paul@314 359
paul@314 360
        elif isinstance(n, compiler.ast.From):
paul@314 361
            self.process_from_node(n)
paul@314 362
paul@113 363
        # Nodes using operator module functions.
paul@113 364
paul@113 365
        elif isinstance(n, compiler.ast.Operator):
paul@113 366
            return self.process_operator_node(n)
paul@113 367
paul@113 368
        elif isinstance(n, compiler.ast.AugAssign):
paul@113 369
            self.process_augassign_node(n)
paul@113 370
paul@113 371
        elif isinstance(n, compiler.ast.Compare):
paul@113 372
            return self.process_compare_node(n)
paul@113 373
paul@113 374
        elif isinstance(n, compiler.ast.Slice):
paul@113 375
            return self.process_slice_node(n)
paul@113 376
paul@113 377
        elif isinstance(n, compiler.ast.Sliceobj):
paul@113 378
            return self.process_sliceobj_node(n)
paul@113 379
paul@113 380
        elif isinstance(n, compiler.ast.Subscript):
paul@113 381
            return self.process_subscript_node(n)
paul@113 382
paul@113 383
        # Classes are visited, but may be ignored if inside functions.
paul@113 384
paul@113 385
        elif isinstance(n, compiler.ast.Class):
paul@113 386
            self.process_class_node(n)
paul@113 387
paul@113 388
        # Functions within namespaces have any dynamic defaults initialised.
paul@113 389
paul@113 390
        elif isinstance(n, compiler.ast.Function):
paul@113 391
            self.process_function_node(n)
paul@113 392
paul@113 393
        # Lambdas are replaced with references to separately-generated
paul@113 394
        # functions.
paul@113 395
paul@113 396
        elif isinstance(n, compiler.ast.Lambda):
paul@113 397
            return self.process_lambda_node(n)
paul@113 398
paul@113 399
        # Assignments.
paul@113 400
paul@113 401
        elif isinstance(n, compiler.ast.Assign):
paul@113 402
paul@113 403
            # Handle each assignment node.
paul@113 404
paul@113 405
            for node in n.nodes:
paul@113 406
                self.process_assignment_node(node, n.expr)
paul@113 407
paul@113 408
        # Accesses.
paul@113 409
paul@113 410
        elif isinstance(n, compiler.ast.Getattr):
paul@113 411
            return self.process_attribute_access(n)
paul@113 412
paul@113 413
        # Names.
paul@113 414
paul@113 415
        elif isinstance(n, compiler.ast.Name):
paul@113 416
            return self.process_name_node(n)
paul@113 417
paul@113 418
        # Loops and conditionals.
paul@113 419
paul@113 420
        elif isinstance(n, compiler.ast.For):
paul@113 421
            self.process_for_node(n)
paul@113 422
paul@113 423
        elif isinstance(n, compiler.ast.While):
paul@113 424
            self.process_while_node(n)
paul@113 425
paul@113 426
        elif isinstance(n, compiler.ast.If):
paul@113 427
            self.process_if_node(n)
paul@113 428
paul@113 429
        elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
paul@113 430
            return self.process_logical_node(n)
paul@113 431
paul@113 432
        elif isinstance(n, compiler.ast.Not):
paul@113 433
            return self.process_not_node(n)
paul@113 434
paul@113 435
        # Exception control-flow tracking.
paul@113 436
paul@113 437
        elif isinstance(n, compiler.ast.TryExcept):
paul@113 438
            self.process_try_node(n)
paul@113 439
paul@113 440
        elif isinstance(n, compiler.ast.TryFinally):
paul@113 441
            self.process_try_finally_node(n)
paul@113 442
paul@113 443
        # Control-flow modification statements.
paul@113 444
paul@113 445
        elif isinstance(n, compiler.ast.Break):
paul@128 446
            self.writestmt("break;")
paul@113 447
paul@113 448
        elif isinstance(n, compiler.ast.Continue):
paul@128 449
            self.writestmt("continue;")
paul@113 450
paul@144 451
        elif isinstance(n, compiler.ast.Raise):
paul@144 452
            self.process_raise_node(n)
paul@144 453
paul@113 454
        elif isinstance(n, compiler.ast.Return):
paul@144 455
            return self.process_return_node(n)
paul@113 456
paul@173 457
        # Print statements.
paul@173 458
paul@173 459
        elif isinstance(n, (compiler.ast.Print, compiler.ast.Printnl)):
paul@173 460
            self.statement(self.process_print_node(n))
paul@173 461
paul@113 462
        # Invocations.
paul@113 463
paul@113 464
        elif isinstance(n, compiler.ast.CallFunc):
paul@113 465
            return self.process_invocation_node(n)
paul@113 466
paul@113 467
        elif isinstance(n, compiler.ast.Keyword):
paul@113 468
            return self.process_structure_node(n.expr)
paul@113 469
paul@113 470
        # Constant usage.
paul@113 471
paul@113 472
        elif isinstance(n, compiler.ast.Const):
paul@405 473
            return self.get_literal_instance(n)
paul@113 474
paul@113 475
        elif isinstance(n, compiler.ast.Dict):
paul@113 476
            return self.get_literal_instance(n, "dict")
paul@113 477
paul@113 478
        elif isinstance(n, compiler.ast.List):
paul@113 479
            return self.get_literal_instance(n, "list")
paul@113 480
paul@113 481
        elif isinstance(n, compiler.ast.Tuple):
paul@113 482
            return self.get_literal_instance(n, "tuple")
paul@113 483
paul@113 484
        # All other nodes are processed depth-first.
paul@113 485
paul@113 486
        else:
paul@144 487
            return self.process_structure(n)
paul@113 488
paul@113 489
    def process_assignment_node(self, n, expr):
paul@113 490
paul@113 491
        "Process the individual node 'n' to be assigned the contents of 'expr'."
paul@113 492
paul@113 493
        # Names and attributes are assigned the entire expression.
paul@113 494
paul@113 495
        if isinstance(n, compiler.ast.AssName):
paul@113 496
            name_ref = self.process_name_node(n, self.process_structure_node(expr))
paul@113 497
            self.statement(name_ref)
paul@113 498
paul@238 499
            # Employ guards after assignments if required.
paul@238 500
paul@238 501
            if expr and name_ref.is_name():
paul@238 502
                self.generate_guard(name_ref.name)
paul@238 503
paul@113 504
        elif isinstance(n, compiler.ast.AssAttr):
paul@124 505
            in_assignment = self.in_assignment
paul@124 506
            self.in_assignment = self.process_structure_node(expr)
paul@124 507
            self.statement(self.process_attribute_access(n))
paul@124 508
            self.in_assignment = in_assignment
paul@113 509
paul@113 510
        # Lists and tuples are matched against the expression and their
paul@113 511
        # items assigned to expression items.
paul@113 512
paul@113 513
        elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
paul@113 514
            self.process_assignment_node_items(n, expr)
paul@113 515
paul@113 516
        # Slices and subscripts are permitted within assignment nodes.
paul@113 517
paul@113 518
        elif isinstance(n, compiler.ast.Slice):
paul@113 519
            self.statement(self.process_slice_node(n, expr))
paul@113 520
paul@113 521
        elif isinstance(n, compiler.ast.Subscript):
paul@113 522
            self.statement(self.process_subscript_node(n, expr))
paul@113 523
paul@124 524
    def process_attribute_access(self, n):
paul@113 525
paul@368 526
        "Process the given attribute access node 'n'."
paul@113 527
paul@113 528
        # Obtain any completed chain and return the reference to it.
paul@113 529
paul@113 530
        attr_expr = self.process_attribute_chain(n)
paul@113 531
        if self.have_access_expression(n):
paul@113 532
            return attr_expr
paul@113 533
paul@113 534
        # Where the start of the chain of attributes has been reached, process
paul@113 535
        # the complete access.
paul@113 536
paul@113 537
        name_ref = attr_expr and attr_expr.is_name() and attr_expr
paul@603 538
        name = name_ref and self.get_name_for_tracking(name_ref.name, name_ref) or None
paul@113 539
paul@553 540
        location = self.get_access_location(name, self.attrs)
paul@113 541
        refs = self.get_referenced_attributes(location)
paul@113 542
paul@113 543
        # Generate access instructions.
paul@113 544
paul@113 545
        subs = {
paul@491 546
            "<expr>" : attr_expr,
paul@757 547
            "<name>" : attr_expr,
paul@491 548
            "<assexpr>" : self.in_assignment,
paul@482 549
            }
paul@482 550
paul@593 551
        subs.update(self.temp_subs)
paul@593 552
        subs.update(self.op_subs)
paul@482 553
paul@113 554
        output = []
paul@482 555
        substituted = set()
paul@482 556
paul@591 557
        # The context set or retrieved will be that used by any enclosing
paul@591 558
        # invocation.
paul@591 559
paul@858 560
        accessor_index = self.accessor_index
paul@828 561
        context_index = self.context_index
paul@618 562
        context_identity = None
paul@776 563
        context_identity_verified = False
paul@752 564
        final_identity = None
paul@828 565
        accessor_test = False
paul@858 566
        accessor_stored = False
paul@591 567
paul@482 568
        # Obtain encoded versions of each instruction, accumulating temporary
paul@482 569
        # variables.
paul@113 570
paul@653 571
        for instruction in self.deducer.access_instructions[location]:
paul@618 572
paul@618 573
            # Intercept a special instruction identifying the context.
paul@618 574
paul@776 575
            if instruction[0] in ("<context_identity>", "<context_identity_verified>"):
paul@858 576
                context_identity, _substituted = \
paul@858 577
                    encode_access_instruction_arg(instruction[1], subs, instruction[0],
paul@858 578
                                                  accessor_index, context_index)
paul@776 579
                context_identity_verified = instruction[0] == "<context_identity_verified>"
paul@618 580
                continue
paul@618 581
paul@752 582
            # Intercept a special instruction identifying the target. The value
paul@752 583
            # is not encoded since it is used internally.
paul@752 584
paul@776 585
            elif instruction[0] == "<final_identity>":
paul@752 586
                final_identity = instruction[1]
paul@752 587
                continue
paul@752 588
paul@828 589
            # Modify test instructions.
paul@828 590
paul@828 591
            elif instruction[0] in typename_ops or instruction[0] in type_ops:
paul@828 592
                instruction = ("__to_error", instruction)
paul@828 593
                accessor_test = True
paul@828 594
paul@858 595
            # Intercept accessor storage.
paul@858 596
paul@858 597
            elif instruction[0] == "<set_accessor>":
paul@858 598
                accessor_stored = True
paul@858 599
paul@618 600
            # Collect the encoded instruction, noting any temporary variables
paul@618 601
            # required by it.
paul@618 602
paul@858 603
            encoded, _substituted = encode_access_instruction(instruction, subs,
paul@858 604
                                        accessor_index, context_index)
paul@482 605
            output.append(encoded)
paul@482 606
            substituted.update(_substituted)
paul@482 607
paul@482 608
        # Record temporary name usage.
paul@482 609
paul@482 610
        for sub in substituted:
paul@593 611
            if self.temp_subs.has_key(sub):
paul@593 612
                self.record_temp(self.temp_subs[sub])
paul@482 613
paul@752 614
        # Get full final identity details.
paul@752 615
paul@752 616
        if final_identity and not refs:
paul@843 617
            refs = set([self.importer.identify(final_identity)])
paul@752 618
paul@113 619
        del self.attrs[0]
paul@858 620
        return AttrResult(output, refs, location,
paul@858 621
                          context_identity, context_identity_verified,
paul@858 622
                          accessor_test, accessor_stored)
paul@113 623
paul@593 624
    def init_substitutions(self):
paul@593 625
paul@593 626
        """
paul@593 627
        Initialise substitutions, defining temporary variable mappings, some of
paul@593 628
        which are also used as substitutions, together with operation mappings
paul@593 629
        used as substitutions in instructions defined by the optimiser.
paul@593 630
        """
paul@593 631
paul@593 632
        self.temp_subs = {
paul@593 633
paul@593 634
            # Substitutions used by instructions.
paul@593 635
paul@593 636
            "<private_context>" : "__tmp_private_context",
paul@593 637
            "<target_accessor>" : "__tmp_target_value",
paul@593 638
paul@593 639
            # Mappings to be replaced by those given below.
paul@593 640
paul@858 641
            "<accessor>" : "__tmp_values",
paul@593 642
            "<context>" : "__tmp_contexts",
paul@601 643
            "<test_context_revert>" : "__tmp_contexts",
paul@595 644
            "<test_context_static>" : "__tmp_contexts",
paul@593 645
            "<set_context>" : "__tmp_contexts",
paul@593 646
            "<set_private_context>" : "__tmp_private_context",
paul@858 647
            "<set_accessor>" : "__tmp_values",
paul@593 648
            "<set_target_accessor>" : "__tmp_target_value",
paul@593 649
            }
paul@593 650
paul@593 651
        self.op_subs = {
paul@858 652
            "<accessor>" : "__get_accessor",
paul@593 653
            "<context>" : "__get_context",
paul@601 654
            "<test_context_revert>" : "__test_context_revert",
paul@595 655
            "<test_context_static>" : "__test_context_static",
paul@593 656
            "<set_context>" : "__set_context",
paul@593 657
            "<set_private_context>" : "__set_private_context",
paul@593 658
            "<set_accessor>" : "__set_accessor",
paul@593 659
            "<set_target_accessor>" : "__set_target_accessor",
paul@593 660
            }
paul@593 661
paul@113 662
    def get_referenced_attributes(self, location):
paul@113 663
paul@113 664
        """
paul@113 665
        Convert 'location' to the form used by the deducer and retrieve any
paul@553 666
        identified attributes.
paul@113 667
        """
paul@113 668
paul@747 669
        # Determine whether any deduced references refer to the accessed
paul@747 670
        # attribute.
paul@747 671
paul@791 672
        attrnames = location.attrnames
paul@747 673
        attrnames = attrnames and attrnames.split(".")
paul@747 674
        remaining = attrnames and len(attrnames) > 1
paul@747 675
paul@751 676
        access_location = self.deducer.const_accesses.get(location)
paul@751 677
paul@747 678
        if remaining and not access_location:
paul@843 679
            return set()
paul@843 680
paul@843 681
        return self.deducer.get_references_for_access(access_location or location)
paul@113 682
paul@553 683
    def get_referenced_attribute_invocations(self, location):
paul@553 684
paul@553 685
        """
paul@553 686
        Convert 'location' to the form used by the deducer and retrieve any
paul@553 687
        identified attribute invocation details.
paul@553 688
        """
paul@553 689
paul@553 690
        access_location = self.deducer.const_accesses.get(location)
paul@553 691
        return self.deducer.reference_invocations_unsuitable.get(access_location or location)
paul@553 692
paul@736 693
    def get_accessor_kinds(self, location):
paul@736 694
paul@736 695
        "Return the accessor kinds for 'location'."
paul@736 696
paul@736 697
        return self.deducer.accessor_kinds.get(location)
paul@234 698
paul@553 699
    def get_access_location(self, name, attrnames=None):
paul@113 700
paul@113 701
        """
paul@553 702
        Using the current namespace, the given 'name', and the 'attrnames'
paul@553 703
        employed in an access, return the access location.
paul@113 704
        """
paul@113 705
paul@113 706
        path = self.get_path_for_access()
paul@113 707
paul@113 708
        # Get the location used by the deducer and optimiser and find any
paul@113 709
        # recorded access.
paul@113 710
paul@553 711
        attrnames = attrnames and ".".join(self.attrs)
paul@113 712
        access_number = self.get_access_number(path, name, attrnames)
paul@113 713
        self.update_access_number(path, name, attrnames)
paul@791 714
        return AccessLocation(path, name, attrnames, access_number)
paul@113 715
paul@113 716
    def get_access_number(self, path, name, attrnames):
paul@113 717
        access = name, attrnames
paul@113 718
        if self.attr_accesses.has_key(path) and self.attr_accesses[path].has_key(access):
paul@113 719
            return self.attr_accesses[path][access]
paul@113 720
        else:
paul@113 721
            return 0
paul@113 722
paul@113 723
    def update_access_number(self, path, name, attrnames):
paul@113 724
        access = name, attrnames
paul@113 725
        if name:
paul@113 726
            init_item(self.attr_accesses, path, dict)
paul@144 727
            init_item(self.attr_accesses[path], access, lambda: 0)
paul@144 728
            self.attr_accesses[path][access] += 1
paul@113 729
paul@237 730
    def get_accessor_location(self, name):
paul@237 731
paul@237 732
        """
paul@237 733
        Using the current namespace and the given 'name', return the accessor
paul@237 734
        location.
paul@237 735
        """
paul@237 736
paul@237 737
        path = self.get_path_for_access()
paul@237 738
paul@237 739
        # Get the location used by the deducer and optimiser and find any
paul@237 740
        # recorded accessor.
paul@237 741
paul@791 742
        version = self.get_accessor_number(path, name)
paul@237 743
        self.update_accessor_number(path, name)
paul@791 744
        return Location(path, name, None, version)
paul@237 745
paul@237 746
    def get_accessor_number(self, path, name):
paul@237 747
        if self.attr_accessors.has_key(path) and self.attr_accessors[path].has_key(name):
paul@237 748
            return self.attr_accessors[path][name]
paul@237 749
        else:
paul@237 750
            return 0
paul@237 751
paul@237 752
    def update_accessor_number(self, path, name):
paul@237 753
        if name:
paul@237 754
            init_item(self.attr_accessors, path, dict)
paul@237 755
            init_item(self.attr_accessors[path], name, lambda: 0)
paul@237 756
            self.attr_accessors[path][name] += 1
paul@237 757
paul@113 758
    def process_class_node(self, n):
paul@113 759
paul@113 760
        "Process the given class node 'n'."
paul@113 761
paul@320 762
        class_name = self.get_object_path(n.name)
paul@320 763
paul@320 764
        # Where a class is set conditionally or where the name may refer to
paul@320 765
        # different values, assign the name.
paul@320 766
paul@320 767
        ref = self.importer.identify(class_name)
paul@320 768
paul@320 769
        if not ref.static():
paul@626 770
            self.process_assignment_for_object(n.name,
paul@626 771
                make_expression("__ATTRVALUE(&%s)" % encode_path(class_name)))
paul@320 772
paul@113 773
        self.enter_namespace(n.name)
paul@113 774
paul@113 775
        if self.have_object():
paul@113 776
            self.write_comment("Class: %s" % class_name)
paul@113 777
paul@257 778
            self.initialise_inherited_members(class_name)
paul@257 779
paul@113 780
            self.process_structure(n)
paul@257 781
            self.write_comment("End class: %s" % class_name)
paul@113 782
paul@113 783
        self.exit_namespace()
paul@113 784
paul@257 785
    def initialise_inherited_members(self, class_name):
paul@257 786
paul@257 787
        "Initialise members of 'class_name' inherited from its ancestors."
paul@257 788
paul@257 789
        for name, path in self.importer.all_class_attrs[class_name].items():
paul@257 790
            target = "%s.%s" % (class_name, name)
paul@257 791
paul@257 792
            # Ignore attributes with definitions.
paul@257 793
paul@257 794
            ref = self.importer.identify(target)
paul@257 795
            if ref:
paul@257 796
                continue
paul@257 797
paul@320 798
            # Ignore special type attributes.
paul@320 799
paul@320 800
            if is_type_attribute(name):
paul@320 801
                continue
paul@320 802
paul@257 803
            # Reference inherited attributes.
paul@257 804
paul@257 805
            ref = self.importer.identify(path)
paul@257 806
            if ref and not ref.static():
paul@257 807
                parent, attrname = path.rsplit(".", 1)
paul@257 808
paul@257 809
                self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % (
paul@624 810
                    encode_path(class_name), name,
paul@624 811
                    encode_path(parent), attrname
paul@257 812
                    ))
paul@257 813
paul@314 814
    def process_from_node(self, n):
paul@314 815
paul@314 816
        "Process the given node 'n', importing from another module."
paul@314 817
paul@314 818
        path = self.get_namespace_path()
paul@314 819
paul@314 820
        # Attempt to obtain the referenced objects.
paul@314 821
paul@314 822
        for name, alias in n.names:
paul@314 823
            if name == "*":
paul@314 824
                raise InspectError("Only explicitly specified names can be imported from modules.", path, n)
paul@314 825
paul@314 826
            # Obtain the path of the assigned name.
paul@314 827
paul@314 828
            objpath = self.get_object_path(alias or name)
paul@314 829
paul@314 830
            # Obtain the identity of the name.
paul@314 831
paul@314 832
            ref = self.importer.identify(objpath)
paul@314 833
paul@314 834
            # Where the name is not static, assign the value.
paul@314 835
paul@314 836
            if ref and not ref.static() and ref.get_name():
paul@314 837
                self.writestmt("%s;" % 
paul@314 838
                    TrResolvedNameRef(alias or name, Reference("<var>", None, objpath),
paul@314 839
                                      expr=TrResolvedNameRef(name, ref)))
paul@314 840
paul@113 841
    def process_function_body_node(self, n):
paul@113 842
paul@113 843
        """
paul@113 844
        Process the given function, lambda, if expression or list comprehension
paul@113 845
        node 'n', generating the body.
paul@113 846
        """
paul@113 847
paul@113 848
        function_name = self.get_namespace_path()
paul@113 849
        self.start_function(function_name)
paul@113 850
paul@113 851
        # Process the function body.
paul@113 852
paul@113 853
        in_conditional = self.in_conditional
paul@113 854
        self.in_conditional = False
paul@192 855
        self.function_target = 0
paul@828 856
        self.max_function_target = 0
paul@828 857
        self.context_index = 0
paul@828 858
        self.max_context_index = 0
paul@858 859
        self.accessor_index = 0
paul@858 860
        self.max_accessor_index = 0
paul@113 861
paul@670 862
        # Volatile locals for exception handling.
paul@670 863
paul@670 864
        self.volatile_locals = set()
paul@670 865
paul@237 866
        # Process any guards defined for the parameters.
paul@237 867
paul@237 868
        for name in self.importer.function_parameters.get(function_name):
paul@238 869
            self.generate_guard(name)
paul@237 870
paul@816 871
        # Also support self in methods, since some mix-in methods may only work
paul@816 872
        # with certain descendant classes.
paul@816 873
paul@816 874
        if self.in_method():
paul@816 875
            self.generate_guard("self")
paul@816 876
paul@819 877
        # Make assignments for .name entries in the parameters, provided this is
paul@819 878
        # a method.
paul@819 879
paul@819 880
        if self.in_method():
paul@819 881
            for name in self.importer.function_attr_initialisers.get(function_name) or []:
paul@819 882
                self.process_assignment_node(
paul@819 883
                    compiler.ast.AssAttr(compiler.ast.Name("self"), name, "OP_ASSIGN"),
paul@819 884
                    compiler.ast.Name(name))
paul@819 885
paul@237 886
        # Produce the body and any additional return statement.
paul@237 887
paul@986 888
        expr = self.process_statement_node(n.code) or \
paul@669 889
               self.in_method() and \
paul@669 890
                   function_name.rsplit(".", 1)[-1] == "__init__" and \
paul@669 891
                   TrResolvedNameRef("self", self.importer.function_locals[function_name]["self"]) or \
paul@669 892
               PredefinedConstantRef("None")
paul@669 893
paul@144 894
        if not isinstance(expr, ReturnRef):
paul@128 895
            self.writestmt("return %s;" % expr)
paul@113 896
paul@113 897
        self.in_conditional = in_conditional
paul@113 898
paul@144 899
        self.end_function(function_name)
paul@113 900
paul@238 901
    def generate_guard(self, name):
paul@238 902
paul@238 903
        """
paul@238 904
        Get the accessor details for 'name', found in the current namespace, and
paul@238 905
        generate any guards defined for it.
paul@238 906
        """
paul@238 907
paul@238 908
        # Obtain the location, keeping track of assignment versions.
paul@238 909
paul@238 910
        location = self.get_accessor_location(name)
paul@238 911
        test = self.deducer.accessor_guard_tests.get(location)
paul@238 912
paul@238 913
        # Generate any guard from the deduced information.
paul@238 914
paul@238 915
        if test:
paul@238 916
            guard, guard_type = test
paul@238 917
paul@238 918
            if guard == "specific":
paul@238 919
                ref = first(self.deducer.accessor_all_types[location])
paul@238 920
                argstr = "&%s" % encode_path(ref.get_origin())
paul@238 921
            elif guard == "common":
paul@238 922
                ref = first(self.deducer.accessor_all_general_types[location])
paul@624 923
                argstr = encode_path(encode_type_attribute(ref.get_origin()))
paul@238 924
            else:
paul@238 925
                return
paul@238 926
paul@238 927
            # Write a test that raises a TypeError upon failure.
paul@238 928
paul@757 929
            self.writestmt("if (!__test_%s_%s(__VALUE(%s), %s)) __raise_type_error();" % (
paul@763 930
                guard, guard_type, encode_path(name), argstr))
paul@238 931
paul@113 932
    def process_function_node(self, n):
paul@113 933
paul@113 934
        """
paul@113 935
        Process the given function, lambda, if expression or list comprehension
paul@113 936
        node 'n', generating any initialisation statements.
paul@113 937
        """
paul@113 938
paul@113 939
        # Where a function is declared conditionally, use a separate name for
paul@113 940
        # the definition, and assign the definition to the stated name.
paul@113 941
paul@196 942
        original_name = n.name
paul@196 943
paul@113 944
        if self.in_conditional or self.in_function:
paul@113 945
            name = self.get_lambda_name()
paul@113 946
        else:
paul@113 947
            name = n.name
paul@113 948
paul@196 949
        objpath = self.get_object_path(name)
paul@196 950
paul@113 951
        # Obtain details of the defaults.
paul@113 952
paul@285 953
        defaults = self.process_function_defaults(n, name, objpath)
paul@113 954
        if defaults:
paul@113 955
            for default in defaults:
paul@113 956
                self.writeline("%s;" % default)
paul@113 957
paul@196 958
        # Where a function is set conditionally or where the name may refer to
paul@196 959
        # different values, assign the name.
paul@196 960
paul@196 961
        ref = self.importer.identify(objpath)
paul@113 962
paul@196 963
        if self.in_conditional or self.in_function:
paul@320 964
            self.process_assignment_for_object(original_name, compiler.ast.Name(name))
paul@196 965
        elif not ref.static():
paul@267 966
            context = self.is_method(objpath)
paul@267 967
paul@320 968
            self.process_assignment_for_object(original_name,
paul@626 969
                make_expression("__ATTRVALUE(&%s)" % encode_path(objpath)))
paul@113 970
paul@285 971
    def process_function_defaults(self, n, name, objpath, instance_name=None):
paul@113 972
paul@113 973
        """
paul@113 974
        Process the given function or lambda node 'n', initialising defaults
paul@113 975
        that are dynamically set. The given 'name' indicates the name of the
paul@285 976
        function. The given 'objpath' indicates the origin of the function.
paul@285 977
        The given 'instance_name' indicates the name of any separate instance
paul@285 978
        of the function created to hold the defaults.
paul@113 979
paul@113 980
        Return a list of operations setting defaults on a function instance.
paul@113 981
        """
paul@113 982
paul@113 983
        function_name = self.get_object_path(name)
paul@113 984
        function_defaults = self.importer.function_defaults.get(function_name)
paul@113 985
        if not function_defaults:
paul@113 986
            return None
paul@113 987
paul@113 988
        # Determine whether any unidentified defaults are involved.
paul@113 989
paul@285 990
        for argname, default in function_defaults:
paul@285 991
            if not default.static():
paul@285 992
                break
paul@285 993
        else:
paul@113 994
            return None
paul@113 995
paul@285 996
        # Handle bound methods.
paul@285 997
paul@285 998
        if not instance_name:
paul@523 999
            instance_name = "&%s" % encode_path(objpath)
paul@757 1000
        else:
paul@757 1001
            instance_name = "__VALUE(%s)" % instance_name
paul@285 1002
paul@113 1003
        # Where defaults are involved but cannot be identified, obtain a new
paul@113 1004
        # instance of the lambda and populate the defaults.
paul@113 1005
paul@113 1006
        defaults = []
paul@113 1007
paul@113 1008
        # Join the original defaults with the inspected defaults.
paul@113 1009
paul@113 1010
        original_defaults = [(argname, default) for (argname, default) in compiler.ast.get_defaults(n) if default]
paul@113 1011
paul@113 1012
        for i, (original, inspected) in enumerate(map(None, original_defaults, function_defaults)):
paul@113 1013
paul@113 1014
            # Obtain any reference for the default.
paul@113 1015
paul@113 1016
            if original:
paul@113 1017
                argname, default = original
paul@113 1018
                name_ref = self.process_structure_node(default)
paul@113 1019
            elif inspected:
paul@113 1020
                argname, default = inspected
paul@113 1021
                name_ref = TrResolvedNameRef(argname, default)
paul@113 1022
            else:
paul@113 1023
                continue
paul@113 1024
paul@338 1025
            # Generate default initialisers except when constants are employed.
paul@338 1026
            # Constants should be used when populating the function structures.
paul@338 1027
paul@338 1028
            if name_ref and not isinstance(name_ref, TrConstantValueRef):
paul@285 1029
                defaults.append("__SETDEFAULT(%s, %s, %s)" % (instance_name, i, name_ref))
paul@113 1030
paul@113 1031
        return defaults
paul@113 1032
paul@113 1033
    def process_if_node(self, n):
paul@113 1034
paul@113 1035
        """
paul@113 1036
        Process the given "if" node 'n'.
paul@113 1037
        """
paul@113 1038
paul@113 1039
        first = True
paul@113 1040
        for test, body in n.tests:
paul@986 1041
            test_ref = self.process_statement_node(test)
paul@113 1042
            self.start_if(first, test_ref)
paul@113 1043
paul@113 1044
            in_conditional = self.in_conditional
paul@113 1045
            self.in_conditional = True
paul@986 1046
            self.process_statement_node(body)
paul@113 1047
            self.in_conditional = in_conditional
paul@113 1048
paul@113 1049
            self.end_if()
paul@113 1050
            first = False
paul@113 1051
paul@113 1052
        if n.else_:
paul@113 1053
            self.start_else()
paul@986 1054
            self.process_statement_node(n.else_)
paul@113 1055
            self.end_else()
paul@113 1056
paul@634 1057
        print >>self.out
paul@634 1058
paul@113 1059
    def process_invocation_node(self, n):
paul@113 1060
paul@113 1061
        "Process the given invocation node 'n'."
paul@113 1062
paul@590 1063
        # Process the expression.
paul@590 1064
paul@113 1065
        expr = self.process_structure_node(n.node)
paul@590 1066
paul@590 1067
        # Obtain details of the invocation expression.
paul@590 1068
paul@113 1069
        objpath = expr.get_origin()
paul@554 1070
        location = expr.access_location()
paul@745 1071
        refs = expr.references()
paul@552 1072
paul@552 1073
        # Identified target details.
paul@552 1074
paul@118 1075
        target = None
paul@407 1076
        target_structure = None
paul@552 1077
paul@552 1078
        # Specific function target information.
paul@552 1079
paul@242 1080
        function = None
paul@552 1081
paul@552 1082
        # Instantiation involvement.
paul@552 1083
paul@317 1084
        instantiation = False
paul@159 1085
        literal_instantiation = False
paul@552 1086
paul@552 1087
        # Invocation requirements.
paul@552 1088
paul@312 1089
        context_required = True
paul@587 1090
        have_access_context = isinstance(expr, AttrResult)
paul@852 1091
paul@852 1092
        # The context identity is merely the thing providing the context.
paul@852 1093
        # A verified context is one that does not need further testing for
paul@852 1094
        # suitability.
paul@852 1095
paul@618 1096
        context_identity = have_access_context and expr.context()
paul@776 1097
        context_verified = have_access_context and expr.context_verified()
paul@852 1098
paul@852 1099
        # The presence of any test operations in the accessor expression.
paul@852 1100
        # With such operations present, the expression cannot be eliminated.
paul@852 1101
paul@828 1102
        tests_accessor = have_access_context and expr.tests_accessor()
paul@858 1103
        stores_accessor = have_access_context and expr.stores_accessor()
paul@852 1104
paul@852 1105
        # Parameter details and parameter list dimensions.
paul@852 1106
paul@552 1107
        parameters = None
paul@753 1108
        num_parameters = None
paul@753 1109
        num_defaults = None
paul@552 1110
paul@552 1111
        # Obtain details of the callable and of its parameters.
paul@113 1112
paul@159 1113
        # Literals may be instantiated specially.
paul@159 1114
paul@159 1115
        if expr.is_name() and expr.name.startswith("$L") and objpath:
paul@317 1116
            instantiation = literal_instantiation = objpath
paul@159 1117
            target = encode_literal_instantiator(objpath)
paul@312 1118
            context_required = False
paul@159 1119
paul@159 1120
        # Identified targets employ function pointers directly.
paul@159 1121
paul@159 1122
        elif objpath:
paul@113 1123
            parameters = self.importer.function_parameters.get(objpath)
paul@749 1124
            function_defaults = self.importer.function_defaults.get(objpath)
paul@753 1125
            num_parameters = parameters and len(parameters) or 0
paul@753 1126
            num_defaults = function_defaults and len(function_defaults) or 0
paul@234 1127
paul@234 1128
            # Class invocation involves instantiators.
paul@234 1129
paul@118 1130
            if expr.has_kind("<class>"):
paul@317 1131
                instantiation = objpath
paul@118 1132
                target = encode_instantiator_pointer(objpath)
paul@540 1133
                init_ref = self.importer.all_class_attrs[objpath]["__init__"]
paul@540 1134
                target_structure = "&%s" % encode_path(init_ref)
paul@312 1135
                context_required = False
paul@234 1136
paul@234 1137
            # Only plain functions and bound methods employ function pointers.
paul@234 1138
paul@118 1139
            elif expr.has_kind("<function>"):
paul@242 1140
                function = objpath
paul@234 1141
paul@234 1142
                # Test for functions and methods.
paul@234 1143
paul@407 1144
                context_required = self.is_method(objpath)
paul@685 1145
paul@736 1146
                accessor_kinds = location and self.get_accessor_kinds(location)
paul@685 1147
paul@312 1148
                instance_accessor = accessor_kinds and \
paul@312 1149
                                    len(accessor_kinds) == 1 and \
paul@312 1150
                                    first(accessor_kinds) == "<instance>"
paul@234 1151
paul@407 1152
                # Only identify certain bound methods or functions.
paul@407 1153
paul@407 1154
                if not context_required or instance_accessor:
paul@234 1155
                    target = encode_function_pointer(objpath)
paul@407 1156
paul@407 1157
                # Access bound method defaults even if it is not clear whether
paul@407 1158
                # the accessor is appropriate.
paul@407 1159
paul@523 1160
                target_structure = "&%s" % encode_path(objpath)
paul@312 1161
paul@749 1162
        # Other targets are retrieved at run-time.
paul@749 1163
paul@749 1164
        else:
paul@749 1165
            if location:
paul@791 1166
                attrnames = location.attrnames
paul@749 1167
                attrname = attrnames and attrnames.rsplit(".", 1)[-1]
paul@749 1168
paul@830 1169
                # Determine common aspects of any identifiable targets.
paul@830 1170
paul@830 1171
                if attrname or refs:
paul@749 1172
                    all_params = set()
paul@749 1173
                    all_defaults = set()
paul@753 1174
                    min_params = set()
paul@753 1175
                    max_params = set()
paul@830 1176
paul@830 1177
                    # Employ references from the expression or find all
paul@830 1178
                    # possible attributes for the given attribute name.
paul@830 1179
paul@830 1180
                    refs = refs or self.get_attributes_for_attrname(attrname)
paul@749 1181
paul@749 1182
                    # Obtain parameters and defaults for each possible target.
paul@749 1183
paul@830 1184
                    for ref in refs:
paul@749 1185
                        origin = ref.get_origin()
paul@749 1186
                        params = self.importer.function_parameters.get(origin)
paul@753 1187
paul@749 1188
                        defaults = self.importer.function_defaults.get(origin)
paul@753 1189
                        if defaults is not None:
paul@749 1190
                            all_defaults.add(tuple(defaults))
paul@749 1191
paul@753 1192
                        if params is not None:
paul@753 1193
                            all_params.add(tuple(params))
paul@753 1194
                            min_params.add(len(params) - (defaults and len(defaults) or 0))
paul@753 1195
                            max_params.add(len(params))
paul@753 1196
                        else:
paul@753 1197
                            refs = set()
paul@753 1198
                            break
paul@753 1199
paul@749 1200
                    # Where the parameters and defaults are always the same,
paul@749 1201
                    # permit populating them in advance.
paul@749 1202
paul@753 1203
                    if refs:
paul@753 1204
                        if self.uses_keyword_arguments(n):
paul@753 1205
                            if len(all_params) == 1 and (not all_defaults or len(all_defaults) == 1):
paul@753 1206
                                parameters = first(all_params)
paul@753 1207
                                function_defaults = all_defaults and first(all_defaults) or []
paul@753 1208
                                num_parameters = parameters and len(parameters) or 0
paul@753 1209
                                num_defaults = function_defaults and len(function_defaults) or 0
paul@753 1210
                        else:
paul@753 1211
                            if len(min_params) == 1 and len(max_params) == 1:
paul@753 1212
                                num_parameters = first(max_params)
paul@753 1213
                                num_defaults = first(max_params) - first(min_params)
paul@749 1214
paul@749 1215
            # Some information about the target may be available and be used to
paul@749 1216
            # provide warnings about argument compatibility.
paul@749 1217
paul@749 1218
            if self.importer.give_warning("args"):
paul@749 1219
                unsuitable = self.get_referenced_attribute_invocations(location)
paul@749 1220
paul@749 1221
                if unsuitable:
paul@749 1222
                    for ref in unsuitable:
paul@749 1223
                        _objpath = ref.get_origin()
paul@749 1224
                        print >>sys.stderr, \
paul@749 1225
                            "In %s, at line %d, inappropriate number of " \
paul@749 1226
                            "arguments given. Need %d arguments to call %s." % (
paul@753 1227
                            self.get_namespace_path(), n.lineno,
paul@753 1228
                            len(self.importer.function_parameters[_objpath]),
paul@749 1229
                            _objpath)
paul@113 1230
paul@828 1231
        # Logical statement about available parameter information.
paul@828 1232
paul@828 1233
        known_parameters = num_parameters is not None
paul@828 1234
paul@828 1235
        # The source of context information: target or temporary.
paul@828 1236
paul@828 1237
        need_context_target = context_required and not have_access_context
paul@828 1238
paul@828 1239
        need_context_stored = context_required and context_identity and \
paul@828 1240
                              context_identity.startswith("__get_context")
paul@828 1241
paul@619 1242
        # Determine any readily-accessible target identity.
paul@619 1243
paul@685 1244
        target_named = expr.is_name() and str(expr) or None
paul@828 1245
        target_identity = target or target_named
paul@828 1246
paul@828 1247
        # Use of target information to populate defaults.
paul@828 1248
paul@828 1249
        defaults_target_var = not (parameters and function_defaults is not None) and \
paul@828 1250
                              known_parameters and len(n.args) < num_parameters
paul@828 1251
paul@828 1252
        # Use of a temporary target variable in these situations:
paul@828 1253
        #
paul@828 1254
        # A target provided by an expression needed for defaults.
paul@828 1255
        #
paul@828 1256
        # A target providing the context but not using a name to do so.
paul@828 1257
        #
paul@828 1258
        # A target expression involving the definition of a context which may
paul@828 1259
        # then be evaluated and stored to ensure that the context is available
paul@828 1260
        # during argument evaluation.
paul@828 1261
        #
paul@828 1262
        # An expression featuring an accessor test.
paul@828 1263
paul@828 1264
        need_target_stored = defaults_target_var and not target_identity or \
paul@829 1265
                             need_context_target and not target_identity or \
paul@828 1266
                             need_context_stored or \
paul@828 1267
                             tests_accessor and not target
paul@828 1268
paul@828 1269
        # Define stored target details.
paul@828 1270
paul@685 1271
        target_stored = "__tmp_targets[%d]" % self.function_target
paul@828 1272
        target_var = need_target_stored and target_stored or target_identity
paul@828 1273
paul@828 1274
        if need_target_stored:
paul@619 1275
            self.record_temp("__tmp_targets")
paul@619 1276
paul@828 1277
        if need_context_stored:
paul@828 1278
            self.record_temp("__tmp_contexts")
paul@619 1279
paul@858 1280
        if stores_accessor:
paul@858 1281
            self.record_temp("__tmp_values")
paul@858 1282
paul@122 1283
        # Arguments are presented in a temporary frame array with any context
paul@312 1284
        # always being the first argument. Where it would be unused, it may be
paul@312 1285
        # set to null.
paul@122 1286
paul@312 1287
        if context_required:
paul@587 1288
            if have_access_context:
paul@851 1289
                context_arg = context_identity
paul@587 1290
            else:
paul@851 1291
                context_arg = "__CONTEXT_AS_VALUE(%s)" % target_var
paul@312 1292
        else:
paul@851 1293
            context_arg = "__NULL"
paul@851 1294
paul@851 1295
        args = [context_arg]
paul@957 1296
        reserved_args = 1
paul@312 1297
paul@552 1298
        # Complete the array with null values, permitting tests for a complete
paul@552 1299
        # set of arguments.
paul@552 1300
paul@753 1301
        args += [None] * (num_parameters is None and len(n.args) or num_parameters is not None and num_parameters or 0)
paul@122 1302
        kwcodes = []
paul@122 1303
        kwargs = []
paul@122 1304
paul@192 1305
        # Any invocations in the arguments will store target details in a
paul@192 1306
        # different location.
paul@192 1307
paul@676 1308
        function_target = self.function_target
paul@828 1309
        context_index = self.context_index
paul@858 1310
        accessor_index = self.accessor_index
paul@828 1311
paul@828 1312
        if need_target_stored:
paul@676 1313
            self.next_target()
paul@676 1314
paul@828 1315
        if need_context_stored:
paul@828 1316
            self.next_context()
paul@192 1317
paul@858 1318
        if stores_accessor:
paul@858 1319
            self.next_accessor()
paul@858 1320
paul@858 1321
        in_parameter_list = self.in_parameter_list
paul@858 1322
        self.in_parameter_list = True
paul@858 1323
paul@122 1324
        for i, arg in enumerate(n.args):
paul@122 1325
            argexpr = self.process_structure_node(arg)
paul@122 1326
paul@122 1327
            # Store a keyword argument, either in the argument list or
paul@122 1328
            # in a separate keyword argument list for subsequent lookup.
paul@122 1329
paul@122 1330
            if isinstance(arg, compiler.ast.Keyword):
paul@113 1331
paul@122 1332
                # With knowledge of the target, store the keyword
paul@122 1333
                # argument directly.
paul@122 1334
paul@122 1335
                if parameters:
paul@373 1336
                    try:
paul@373 1337
                        argnum = parameters.index(arg.name)
paul@373 1338
                    except ValueError:
paul@373 1339
                        raise TranslateError("Argument %s is not recognised." % arg.name,
paul@373 1340
                                             self.get_namespace_path(), n)
paul@957 1341
                    args[argnum + reserved_args] = str(argexpr)
paul@122 1342
paul@122 1343
                # Otherwise, store the details in a separate collection.
paul@122 1344
paul@122 1345
                else:
paul@122 1346
                    kwargs.append(str(argexpr))
paul@122 1347
                    kwcodes.append("{%s, %s}" % (
paul@623 1348
                        encode_ppos(arg.name), encode_pcode(arg.name)))
paul@122 1349
paul@312 1350
            # Store non-keyword arguments in the argument list, rejecting
paul@312 1351
            # superfluous arguments.
paul@312 1352
paul@122 1353
            else:
paul@225 1354
                try:
paul@957 1355
                    args[i + reserved_args] = str(argexpr)
paul@225 1356
                except IndexError:
paul@225 1357
                    raise TranslateError("Too many arguments specified.",
paul@225 1358
                                         self.get_namespace_path(), n)
paul@113 1359
paul@192 1360
        # Reference the current target again.
paul@192 1361
paul@858 1362
        self.in_parameter_list = in_parameter_list
paul@858 1363
paul@858 1364
        if not self.in_parameter_list:
paul@858 1365
            self.function_target = function_target
paul@858 1366
            self.context_index = context_index
paul@858 1367
            self.accessor_index = accessor_index
paul@192 1368
paul@113 1369
        # Defaults are added to the frame where arguments are missing.
paul@113 1370
paul@803 1371
        if parameters and function_defaults is not None:
paul@753 1372
paul@753 1373
            # Visit each default and set any missing arguments. Where keyword
paul@753 1374
            # arguments have been used, the defaults must be inspected and, if
paul@753 1375
            # necessary, inserted into gaps in the argument list.
paul@749 1376
paul@749 1377
            for i, (argname, default) in enumerate(function_defaults):
paul@749 1378
                argnum = parameters.index(argname)
paul@957 1379
                if not args[argnum + reserved_args]:
paul@957 1380
                    args[argnum + reserved_args] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
paul@149 1381
paul@753 1382
        elif known_parameters:
paul@753 1383
paul@753 1384
            # No specific parameter details are provided, but no keyword
paul@753 1385
            # arguments are used. Thus, defaults can be supplied using position
paul@753 1386
            # information only.
paul@753 1387
paul@753 1388
            i = len(n.args)
paul@753 1389
            pos = i - (num_parameters - num_defaults)
paul@753 1390
            while i < num_parameters:
paul@957 1391
                args[i + reserved_args] = "__GETDEFAULT(%s.value, %d)" % (target_var, pos)
paul@753 1392
                i += 1
paul@753 1393
                pos += 1
paul@753 1394
paul@173 1395
        # Test for missing arguments.
paul@173 1396
paul@173 1397
        if None in args:
paul@173 1398
            raise TranslateError("Not all arguments supplied.",
paul@173 1399
                                 self.get_namespace_path(), n)
paul@173 1400
paul@149 1401
        # Encode the arguments.
paul@122 1402
paul@669 1403
        # Where literal instantiation is occurring, add an argument indicating
paul@669 1404
        # the number of values. The context is excluded.
paul@669 1405
paul@669 1406
        if literal_instantiation:
paul@957 1407
            argstr = "%d, %s" % (len(args) - reserved_args, ", ".join(args[reserved_args:]))
paul@669 1408
        else:
paul@669 1409
            argstr = ", ".join(args)
paul@669 1410
paul@122 1411
        kwargstr = kwargs and ("__ARGS(%s)" % ", ".join(kwargs)) or "0"
paul@122 1412
        kwcodestr = kwcodes and ("__KWARGS(%s)" % ", ".join(kwcodes)) or "0"
paul@122 1413
paul@156 1414
        # First, the invocation expression is presented.
paul@113 1415
paul@156 1416
        stages = []
paul@828 1417
        emit = stages.append
paul@828 1418
paul@828 1419
        # Assign and yield any stored target.
paul@828 1420
        # The context may be set in the expression.
paul@828 1421
paul@828 1422
        if need_target_stored:
paul@828 1423
            emit("%s = %s" % (target_var, expr))
paul@828 1424
            target_expr = target_var
paul@828 1425
paul@828 1426
        # Otherwise, retain the expression for later use.
paul@828 1427
paul@828 1428
        else:
paul@828 1429
            target_expr = str(expr)
paul@156 1430
paul@685 1431
        # Any specific callable is then obtained for invocation.
paul@156 1432
paul@163 1433
        if target:
paul@828 1434
paul@828 1435
            # An expression involving a test of the accessor providing the target.
paul@828 1436
            # This must be emitted in order to perform the test.
paul@828 1437
paul@828 1438
            if tests_accessor:
paul@828 1439
                emit(str(expr))
paul@828 1440
paul@828 1441
            emit(target)
paul@484 1442
paul@685 1443
        # Methods accessed via unidentified accessors are obtained for
paul@685 1444
        # invocation.
paul@484 1445
paul@242 1446
        elif function:
paul@523 1447
            if context_required:
paul@838 1448
paul@851 1449
                # Avoid further context testing if appropriate.
paul@851 1450
paul@851 1451
                if have_access_context and context_verified:
paul@838 1452
                    emit("__get_function_member(%s)" % target_expr)
paul@838 1453
paul@838 1454
                # Otherwise, test the context for the function/method.
paul@838 1455
paul@587 1456
                else:
paul@851 1457
                    emit("__get_function(%s, %s)" % (context_arg, target_expr))
paul@523 1458
            else:
paul@828 1459
                emit("_get_function_member(%s)" % target_expr)
paul@122 1460
paul@749 1461
        # With known parameters, the target can be tested.
paul@749 1462
paul@753 1463
        elif known_parameters:
paul@749 1464
            if self.always_callable(refs):
paul@851 1465
                if context_verified:
paul@828 1466
                    emit("__get_function_member(%s)" % target_expr)
paul@776 1467
                else:
paul@828 1468
                    emit("__get_function(%s, %s)" % (context_arg, target_expr))
paul@749 1469
            else:
paul@828 1470
                emit("__check_and_get_function(%s, %s)" % (context_arg, target_expr))
paul@749 1471
paul@122 1472
        # With a known target, the function is obtained directly and called.
paul@484 1473
        # By putting the invocation at the end of the final element in the
paul@484 1474
        # instruction sequence (the stages), the result becomes the result of
paul@484 1475
        # the sequence. Moreover, the parameters become part of the sequence
paul@484 1476
        # and thereby participate in a guaranteed evaluation order.
paul@122 1477
paul@753 1478
        if target or function or known_parameters:
paul@498 1479
            stages[-1] += "(%s)" % argstr
paul@498 1480
            if instantiation:
paul@498 1481
                return InstantiationResult(instantiation, stages)
paul@498 1482
            else:
paul@498 1483
                return InvocationResult(stages)
paul@113 1484
paul@122 1485
        # With unknown targets, the generic invocation function is applied to
paul@122 1486
        # the callable and argument collections.
paul@113 1487
paul@122 1488
        else:
paul@828 1489
            emit("__invoke(\n%s,\n%d, %d, %s, %s,\n%d, %s\n)" % (
paul@828 1490
                target_expr,
paul@745 1491
                self.always_callable(refs) and 1 or 0,
paul@122 1492
                len(kwargs), kwcodestr, kwargstr,
paul@664 1493
                len(args), "__ARGS(%s)" % argstr))
paul@498 1494
            return InvocationResult(stages)
paul@113 1495
paul@676 1496
    def next_target(self):
paul@676 1497
paul@676 1498
        "Allocate the next function target storage."
paul@676 1499
paul@676 1500
        self.function_target += 1
paul@828 1501
        self.max_function_target = max(self.function_target, self.max_function_target)
paul@828 1502
paul@828 1503
    def next_context(self):
paul@828 1504
paul@828 1505
        "Allocate the next context value storage."
paul@828 1506
paul@828 1507
        self.context_index += 1
paul@828 1508
        self.max_context_index = max(self.context_index, self.max_context_index)
paul@676 1509
paul@858 1510
    def next_accessor(self):
paul@858 1511
paul@858 1512
        "Allocate the next accessor value storage."
paul@858 1513
paul@858 1514
        self.accessor_index += 1
paul@858 1515
        self.max_accessor_index = max(self.accessor_index, self.max_accessor_index)
paul@858 1516
paul@113 1517
    def always_callable(self, refs):
paul@113 1518
paul@113 1519
        "Determine whether all 'refs' are callable."
paul@113 1520
paul@745 1521
        if not refs:
paul@745 1522
            return False
paul@745 1523
paul@113 1524
        for ref in refs:
paul@748 1525
            if not ref.has_kind("<function>") and not self.importer.get_attributes(ref, "__fn__"):
paul@113 1526
                return False
paul@748 1527
paul@113 1528
        return True
paul@113 1529
paul@113 1530
    def need_default_arguments(self, objpath, nargs):
paul@113 1531
paul@113 1532
        """
paul@113 1533
        Return whether any default arguments are needed when invoking the object
paul@113 1534
        given by 'objpath'.
paul@113 1535
        """
paul@113 1536
paul@113 1537
        parameters = self.importer.function_parameters.get(objpath)
paul@113 1538
        return nargs < len(parameters)
paul@113 1539
paul@753 1540
    def uses_keyword_arguments(self, n):
paul@753 1541
paul@753 1542
        "Return whether invocation node 'n' uses keyword arguments."
paul@753 1543
paul@753 1544
        for arg in enumerate(n.args):
paul@753 1545
            if isinstance(arg, compiler.ast.Keyword):
paul@753 1546
                return True
paul@753 1547
paul@753 1548
        return False
paul@753 1549
paul@749 1550
    def get_attributes_for_attrname(self, attrname):
paul@749 1551
paul@749 1552
        "Return a set of all attributes exposed by 'attrname'."
paul@749 1553
paul@749 1554
        usage = [(attrname, True, False)]
paul@749 1555
        class_types = self.deducer.get_class_types_for_usage(usage)
paul@749 1556
        instance_types = self.deducer.get_instance_types_for_usage(usage)
paul@749 1557
        module_types = self.deducer.get_module_types_for_usage(usage)
paul@749 1558
        attrs = set()
paul@749 1559
paul@749 1560
        for ref in combine_types(class_types, instance_types, module_types):
paul@749 1561
            attrs.update(self.importer.get_attributes(ref, attrname))
paul@749 1562
paul@749 1563
        return attrs
paul@749 1564
paul@113 1565
    def process_lambda_node(self, n):
paul@113 1566
paul@113 1567
        "Process the given lambda node 'n'."
paul@113 1568
paul@113 1569
        name = self.get_lambda_name()
paul@113 1570
        function_name = self.get_object_path(name)
paul@858 1571
        instance_name = "__get_accessor(%d)" % self.accessor_index
paul@858 1572
paul@858 1573
        defaults = self.process_function_defaults(n, name, function_name, instance_name)
paul@149 1574
paul@149 1575
        # Without defaults, produce an attribute referring to the function.
paul@149 1576
paul@113 1577
        if not defaults:
paul@626 1578
            return make_expression("__ATTRVALUE(&%s)" % encode_path(function_name))
paul@149 1579
paul@149 1580
        # With defaults, copy the function structure and set the defaults on the
paul@149 1581
        # copy.
paul@149 1582
paul@113 1583
        else:
paul@858 1584
            self.record_temp("__tmp_values")
paul@858 1585
            return make_expression("""\
paul@858 1586
(__set_accessor(%d, __ATTRVALUE(__COPY(&%s, sizeof(%s)))),
paul@858 1587
 %s,
paul@858 1588
 __get_accessor(%d))""" % (
paul@858 1589
                self.accessor_index,
paul@151 1590
                encode_path(function_name),
paul@151 1591
                encode_symbol("obj", function_name),
paul@858 1592
                ", ".join(defaults),
paul@858 1593
                self.accessor_index))
paul@113 1594
paul@113 1595
    def process_logical_node(self, n):
paul@113 1596
paul@631 1597
        "Process the given operator node 'n'."
paul@113 1598
paul@482 1599
        self.record_temp("__tmp_result")
paul@482 1600
paul@631 1601
        conjunction = isinstance(n, compiler.ast.And)
paul@141 1602
        results = []
paul@113 1603
paul@631 1604
        for node in n.nodes:
paul@631 1605
            results.append(self.process_structure_node(node))
paul@631 1606
paul@631 1607
        return LogicalOperationResult(results, conjunction)
paul@113 1608
paul@113 1609
    def process_name_node(self, n, expr=None):
paul@113 1610
paul@113 1611
        "Process the given name node 'n' with the optional assignment 'expr'."
paul@113 1612
paul@113 1613
        # Determine whether the name refers to a static external entity.
paul@113 1614
paul@113 1615
        if n.name in predefined_constants:
paul@399 1616
            return PredefinedConstantRef(n.name, expr)
paul@113 1617
paul@173 1618
        # Convert literal references, operator function names, and print
paul@173 1619
        # function names to references.
paul@113 1620
paul@173 1621
        elif n.name.startswith("$L") or n.name.startswith("$op") or \
paul@835 1622
             n.name.startswith("$seq") or n.name.startswith("$print"):
paul@423 1623
paul@423 1624
            ref, paths = self.importer.get_module(self.name).special[n.name]
paul@113 1625
            return TrResolvedNameRef(n.name, ref)
paul@113 1626
paul@113 1627
        # Get the appropriate name for the name reference, using the same method
paul@113 1628
        # as in the inspector.
paul@113 1629
paul@250 1630
        path = self.get_namespace_path()
paul@250 1631
        objpath = self.get_object_path(n.name)
paul@250 1632
paul@250 1633
        # Determine any assigned globals.
paul@250 1634
paul@250 1635
        globals = self.importer.get_module(self.name).scope_globals.get(path)
paul@603 1636
paul@603 1637
        # Explicitly declared globals.
paul@603 1638
paul@250 1639
        if globals and n.name in globals:
paul@250 1640
            objpath = self.get_global_path(n.name)
paul@603 1641
            is_global = True
paul@603 1642
paul@603 1643
        # Implicitly referenced globals in functions.
paul@603 1644
paul@603 1645
        elif self.in_function:
paul@603 1646
            is_global = n.name not in self.importer.function_locals[path]
paul@603 1647
paul@603 1648
        # Implicitly referenced globals elsewhere.
paul@603 1649
paul@603 1650
        else:
paul@603 1651
            namespace = self.importer.identify(path)
paul@603 1652
            is_global = not self.importer.get_attributes(namespace, n.name)
paul@113 1653
paul@113 1654
        # Get the static identity of the name.
paul@113 1655
paul@250 1656
        ref = self.importer.identify(objpath)
paul@152 1657
        if ref and not ref.get_name():
paul@250 1658
            ref = ref.alias(objpath)
paul@113 1659
paul@113 1660
        # Obtain any resolved names for non-assignment names.
paul@113 1661
paul@113 1662
        if not expr and not ref and self.in_function:
paul@250 1663
            locals = self.importer.function_locals.get(path)
paul@113 1664
            ref = locals and locals.get(n.name)
paul@113 1665
paul@685 1666
        # Find any invocation or alias details.
paul@553 1667
paul@678 1668
        name = self.get_name_for_tracking(n.name, is_global=is_global)
paul@700 1669
        location = not expr and self.get_access_location(name) or None
paul@553 1670
paul@670 1671
        # Mark any local assignments as volatile in exception blocks.
paul@670 1672
paul@670 1673
        if expr and self.in_function and not is_global and self.in_try_except:
paul@670 1674
            self.make_volatile(n.name)
paul@670 1675
paul@113 1676
        # Qualified names are used for resolved static references or for
paul@113 1677
        # static namespace members. The reference should be configured to return
paul@113 1678
        # such names.
paul@113 1679
paul@685 1680
        name_ref = TrResolvedNameRef(n.name, ref, expr=expr, is_global=is_global,
paul@686 1681
                                     location=location)
paul@734 1682
        return not expr and self.get_aliases(name_ref) or name_ref
paul@685 1683
paul@685 1684
    def get_aliases(self, name_ref):
paul@685 1685
paul@685 1686
        "Return alias references for the given 'name_ref'."
paul@685 1687
paul@685 1688
        location = name_ref.access_location()
paul@831 1689
        accessor_locations = self.deducer.access_index.get(location)
paul@831 1690
paul@831 1691
        if not accessor_locations:
paul@831 1692
            return None
paul@831 1693
paul@831 1694
        refs = set()
paul@831 1695
paul@831 1696
        for accessor_location in accessor_locations:
paul@831 1697
            alias_refs = self.deducer.referenced_objects.get(accessor_location)
paul@831 1698
            if alias_refs:
paul@831 1699
                refs.update(alias_refs)
paul@831 1700
paul@831 1701
        if refs:
paul@831 1702
            return AliasResult(name_ref, refs, location)
paul@831 1703
        else:
paul@831 1704
            return None
paul@113 1705
paul@670 1706
    def make_volatile(self, name):
paul@670 1707
paul@670 1708
        "Record 'name' as volatile in the current namespace."
paul@670 1709
paul@670 1710
        self.volatile_locals.add(name)
paul@670 1711
paul@113 1712
    def process_not_node(self, n):
paul@113 1713
paul@113 1714
        "Process the given operator node 'n'."
paul@113 1715
paul@638 1716
        return self.make_negation(self.process_structure_node(n.expr))
paul@144 1717
paul@144 1718
    def process_raise_node(self, n):
paul@144 1719
paul@144 1720
        "Process the given raise node 'n'."
paul@144 1721
paul@144 1722
        # NOTE: Determine which raise statement variants should be permitted.
paul@144 1723
paul@176 1724
        if n.expr1:
paul@467 1725
paul@467 1726
            # Names with accompanying arguments are treated like invocations.
paul@467 1727
paul@467 1728
            if n.expr2:
paul@467 1729
                call = compiler.ast.CallFunc(n.expr1, [n.expr2])
paul@467 1730
                exc = self.process_structure_node(call)
paul@467 1731
                self.writestmt("__Raise(%s);" % exc)
paul@317 1732
paul@317 1733
            # Raise instances, testing the kind at run-time if necessary and
paul@317 1734
            # instantiating any non-instance.
paul@317 1735
paul@317 1736
            else:
paul@467 1737
                exc = self.process_structure_node(n.expr1)
paul@467 1738
paul@467 1739
                if isinstance(exc, TrInstanceRef):
paul@467 1740
                    self.writestmt("__Raise(%s);" % exc)
paul@467 1741
                else:
paul@467 1742
                    self.writestmt("__Raise(__ensure_instance(%s));" % exc)
paul@176 1743
        else:
paul@346 1744
            self.writestmt("__Throw(__tmp_exc);")
paul@144 1745
paul@144 1746
    def process_return_node(self, n):
paul@144 1747
paul@144 1748
        "Process the given return node 'n'."
paul@144 1749
paul@144 1750
        expr = self.process_structure_node(n.value) or PredefinedConstantRef("None")
paul@189 1751
        if self.in_try_finally or self.in_try_except:
paul@144 1752
            self.writestmt("__Return(%s);" % expr)
paul@144 1753
        else:
paul@144 1754
            self.writestmt("return %s;" % expr)
paul@144 1755
paul@144 1756
        return ReturnRef()
paul@113 1757
paul@113 1758
    def process_try_node(self, n):
paul@113 1759
paul@113 1760
        """
paul@113 1761
        Process the given "try...except" node 'n'.
paul@113 1762
        """
paul@113 1763
paul@189 1764
        in_try_except = self.in_try_except
paul@189 1765
        self.in_try_except = True
paul@189 1766
paul@144 1767
        # Use macros to implement exception handling.
paul@113 1768
paul@144 1769
        self.writestmt("__Try")
paul@113 1770
        self.writeline("{")
paul@113 1771
        self.indent += 1
paul@986 1772
        self.process_statement_node(n.body)
paul@144 1773
paul@144 1774
        # Put the else statement in another try block that handles any raised
paul@144 1775
        # exceptions and converts them to exceptions that will not be handled by
paul@144 1776
        # the main handling block.
paul@144 1777
paul@144 1778
        if n.else_:
paul@144 1779
            self.writestmt("__Try")
paul@144 1780
            self.writeline("{")
paul@144 1781
            self.indent += 1
paul@986 1782
            self.process_statement_node(n.else_)
paul@144 1783
            self.indent -= 1
paul@144 1784
            self.writeline("}")
paul@144 1785
            self.writeline("__Catch (__tmp_exc)")
paul@144 1786
            self.writeline("{")
paul@144 1787
            self.indent += 1
paul@144 1788
            self.writeline("if (__tmp_exc.raising) __RaiseElse(__tmp_exc.arg);")
paul@191 1789
            self.writeline("else if (__tmp_exc.completing) __Throw(__tmp_exc);")
paul@144 1790
            self.indent -= 1
paul@144 1791
            self.writeline("}")
paul@144 1792
paul@144 1793
        # Complete the try block and enter the finally block, if appropriate.
paul@144 1794
paul@144 1795
        if self.in_try_finally:
paul@144 1796
            self.writestmt("__Complete;")
paul@144 1797
paul@113 1798
        self.indent -= 1
paul@113 1799
        self.writeline("}")
paul@113 1800
paul@189 1801
        self.in_try_except = in_try_except
paul@189 1802
paul@144 1803
        # Handlers are tests within a common handler block.
paul@144 1804
paul@144 1805
        self.writeline("__Catch (__tmp_exc)")
paul@144 1806
        self.writeline("{")
paul@144 1807
        self.indent += 1
paul@144 1808
paul@189 1809
        # Introduce an if statement to handle the completion of a try block.
paul@189 1810
paul@189 1811
        self.process_try_completion()
paul@189 1812
paul@144 1813
        # Handle exceptions in else blocks converted to __RaiseElse, converting
paul@144 1814
        # them back to normal exceptions.
paul@144 1815
paul@144 1816
        if n.else_:
paul@189 1817
            self.writeline("else if (__tmp_exc.raising_else) __Raise(__tmp_exc.arg);")
paul@144 1818
paul@144 1819
        # Exception handling.
paul@144 1820
paul@113 1821
        for name, var, handler in n.handlers:
paul@144 1822
paul@144 1823
            # Test for specific exceptions.
paul@144 1824
paul@113 1825
            if name is not None:
paul@986 1826
                name_ref = self.process_statement_node(name)
paul@462 1827
                self.writeline("else if (__ISINSTANCE(__tmp_exc.arg, %s))" % name_ref)
paul@144 1828
            else:
paul@189 1829
                self.writeline("else if (1)")
paul@113 1830
paul@113 1831
            self.writeline("{")
paul@113 1832
            self.indent += 1
paul@113 1833
paul@113 1834
            # Establish the local for the handler.
paul@113 1835
paul@113 1836
            if var is not None:
paul@261 1837
                self.writestmt("%s;" % self.process_name_node(var, make_expression("__tmp_exc.arg")))
paul@113 1838
paul@113 1839
            if handler is not None:
paul@986 1840
                self.process_statement_node(handler)
paul@113 1841
paul@113 1842
            self.indent -= 1
paul@113 1843
            self.writeline("}")
paul@113 1844
paul@144 1845
        # Re-raise unhandled exceptions.
paul@144 1846
paul@189 1847
        self.writeline("else __Throw(__tmp_exc);")
paul@144 1848
paul@144 1849
        # End the handler block.
paul@144 1850
paul@144 1851
        self.indent -= 1
paul@144 1852
        self.writeline("}")
paul@634 1853
        print >>self.out
paul@113 1854
paul@113 1855
    def process_try_finally_node(self, n):
paul@113 1856
paul@113 1857
        """
paul@113 1858
        Process the given "try...finally" node 'n'.
paul@113 1859
        """
paul@113 1860
paul@144 1861
        in_try_finally = self.in_try_finally
paul@144 1862
        self.in_try_finally = True
paul@113 1863
paul@144 1864
        # Use macros to implement exception handling.
paul@144 1865
paul@144 1866
        self.writestmt("__Try")
paul@113 1867
        self.writeline("{")
paul@113 1868
        self.indent += 1
paul@986 1869
        self.process_statement_node(n.body)
paul@113 1870
        self.indent -= 1
paul@113 1871
        self.writeline("}")
paul@144 1872
paul@144 1873
        self.in_try_finally = in_try_finally
paul@144 1874
paul@144 1875
        # Finally clauses handle special exceptions.
paul@144 1876
paul@144 1877
        self.writeline("__Catch (__tmp_exc)")
paul@113 1878
        self.writeline("{")
paul@113 1879
        self.indent += 1
paul@986 1880
        self.process_statement_node(n.final)
paul@144 1881
paul@189 1882
        # Introduce an if statement to handle the completion of a try block.
paul@189 1883
paul@189 1884
        self.process_try_completion()
paul@189 1885
        self.writeline("else __Throw(__tmp_exc);")
paul@189 1886
paul@189 1887
        self.indent -= 1
paul@189 1888
        self.writeline("}")
paul@634 1889
        print >>self.out
paul@189 1890
paul@189 1891
    def process_try_completion(self):
paul@189 1892
paul@189 1893
        "Generate a test for the completion of a try block."
paul@144 1894
paul@144 1895
        self.writestmt("if (__tmp_exc.completing)")
paul@144 1896
        self.writeline("{")
paul@144 1897
        self.indent += 1
paul@189 1898
paul@316 1899
        # Do not return anything at the module level.
paul@316 1900
paul@316 1901
        if self.get_namespace_path() != self.name:
paul@189 1902
paul@316 1903
            # Only use the normal return statement if no surrounding try blocks
paul@316 1904
            # apply.
paul@316 1905
paul@316 1906
            if not self.in_try_finally and not self.in_try_except:
paul@316 1907
                self.writeline("if (!__ISNULL(__tmp_exc.arg)) return __tmp_exc.arg;")
paul@316 1908
            else:
paul@316 1909
                self.writeline("if (!__ISNULL(__tmp_exc.arg)) __Throw(__tmp_exc);")
paul@144 1910
paul@113 1911
        self.indent -= 1
paul@113 1912
        self.writeline("}")
paul@113 1913
paul@113 1914
    def process_while_node(self, n):
paul@113 1915
paul@113 1916
        "Process the given while node 'n'."
paul@113 1917
paul@113 1918
        self.writeline("while (1)")
paul@113 1919
        self.writeline("{")
paul@113 1920
        self.indent += 1
paul@986 1921
        test = self.process_statement_node(n.test)
paul@113 1922
paul@113 1923
        # Emit the loop termination condition unless "while <true value>" is
paul@113 1924
        # indicated.
paul@113 1925
paul@113 1926
        if not (isinstance(test, PredefinedConstantRef) and test.value):
paul@113 1927
paul@629 1928
            # Emit a negated test of the continuation condition.
paul@629 1929
paul@638 1930
            self.start_if(True, self.make_negation(test))
paul@113 1931
            if n.else_:
paul@986 1932
                self.process_statement_node(n.else_)
paul@128 1933
            self.writestmt("break;")
paul@629 1934
            self.end_if()
paul@113 1935
paul@113 1936
        in_conditional = self.in_conditional
paul@113 1937
        self.in_conditional = True
paul@986 1938
        self.process_statement_node(n.body)
paul@113 1939
        self.in_conditional = in_conditional
paul@113 1940
paul@113 1941
        self.indent -= 1
paul@113 1942
        self.writeline("}")
paul@634 1943
        print >>self.out
paul@113 1944
paul@482 1945
    # Special variable usage.
paul@482 1946
paul@637 1947
    def get_temp_path(self):
paul@637 1948
paul@637 1949
        """
paul@637 1950
        Return the appropriate namespace path for temporary names in the current
paul@637 1951
        namespace.
paul@637 1952
        """
paul@637 1953
paul@637 1954
        if self.in_function:
paul@637 1955
            return self.get_namespace_path()
paul@637 1956
        else:
paul@637 1957
            return self.name
paul@637 1958
paul@482 1959
    def record_temp(self, name):
paul@482 1960
paul@482 1961
        """
paul@482 1962
        Record the use of the temporary 'name' in the current namespace. At the
paul@482 1963
        class or module level, the temporary name is associated with the module,
paul@482 1964
        since the variable will then be allocated in the module's own main
paul@482 1965
        program.
paul@482 1966
        """
paul@482 1967
paul@637 1968
        path = self.get_temp_path()
paul@637 1969
paul@637 1970
        init_item(self.temp_usage, path, list)
paul@637 1971
        self.temp_usage[path].append(name)
paul@637 1972
paul@637 1973
    def remove_temps(self, names):
paul@637 1974
paul@637 1975
        """
paul@637 1976
        Remove 'names' from temporary storage allocations, each instance
paul@637 1977
        removing each request for storage.
paul@637 1978
        """
paul@637 1979
paul@637 1980
        path = self.get_temp_path()
paul@637 1981
paul@637 1982
        for name in names:
paul@637 1983
            if self.uses_temp(path, name):
paul@637 1984
                self.temp_usage[path].remove(name)
paul@482 1985
paul@482 1986
    def uses_temp(self, path, name):
paul@482 1987
paul@482 1988
        """
paul@482 1989
        Return whether the given namespace 'path' employs a temporary variable
paul@482 1990
        with the given 'name'. Note that 'path' should only be a module or a
paul@482 1991
        function or method, not a class.
paul@482 1992
        """
paul@482 1993
paul@482 1994
        return self.temp_usage.has_key(path) and name in self.temp_usage[path]
paul@482 1995
paul@638 1996
    def make_negation(self, expr):
paul@638 1997
paul@638 1998
        "Return a negated form of 'expr'."
paul@638 1999
paul@638 2000
        result = NegationResult(expr)
paul@638 2001
paul@638 2002
        # Negation discards the temporary results of its operand.
paul@638 2003
paul@638 2004
        temps = expr.discards_temporary()
paul@638 2005
        if temps:
paul@638 2006
            self.remove_temps(temps)
paul@638 2007
paul@638 2008
        return result
paul@638 2009
paul@113 2010
    # Output generation.
paul@113 2011
paul@128 2012
    def start_output(self):
paul@159 2013
paul@159 2014
        "Write the declarations at the top of each source file."
paul@159 2015
paul@128 2016
        print >>self.out, """\
paul@128 2017
#include "types.h"
paul@144 2018
#include "exceptions.h"
paul@128 2019
#include "ops.h"
paul@128 2020
#include "progconsts.h"
paul@128 2021
#include "progops.h"
paul@128 2022
#include "progtypes.h"
paul@137 2023
#include "main.h"
paul@128 2024
"""
paul@128 2025
paul@482 2026
    def start_unit(self):
paul@482 2027
paul@482 2028
        "Record output within a generated function for later use."
paul@482 2029
paul@482 2030
        self.out = StringIO()
paul@482 2031
paul@670 2032
    def end_unit(self):
paul@670 2033
paul@670 2034
        "Restore the output stream."
paul@482 2035
paul@482 2036
        out = self.out
paul@482 2037
        self.out = self.out_toplevel
paul@670 2038
        return out
paul@670 2039
paul@670 2040
    def flush_unit(self, name, out):
paul@670 2041
paul@670 2042
        "Add declarations and generated code."
paul@482 2043
paul@482 2044
        self.write_temporaries(name)
paul@634 2045
        print >>self.out
paul@482 2046
        out.seek(0)
paul@482 2047
        self.out.write(out.read())
paul@482 2048
paul@113 2049
    def start_module(self):
paul@159 2050
paul@159 2051
        "Write the start of each module's main function."
paul@159 2052
paul@113 2053
        print >>self.out, "void __main_%s()" % encode_path(self.name)
paul@113 2054
        print >>self.out, "{"
paul@113 2055
        self.indent += 1
paul@561 2056
paul@561 2057
        # Define temporary variables, excluded from the module structure itself.
paul@561 2058
paul@561 2059
        tempnames = []
paul@561 2060
paul@561 2061
        for n in self.importer.all_module_attrs[self.name]:
paul@561 2062
            if n.startswith("$t"):
paul@561 2063
                tempnames.append(encode_path(n))
paul@561 2064
paul@561 2065
        if tempnames:
paul@561 2066
            tempnames.sort()
paul@561 2067
            self.writeline("__attr %s;" % ", ".join(tempnames))
paul@561 2068
paul@482 2069
        self.start_unit()
paul@113 2070
paul@113 2071
    def end_module(self):
paul@159 2072
paul@159 2073
        "End each module by closing its main function."
paul@159 2074
paul@670 2075
        out = self.end_unit()
paul@670 2076
        self.flush_unit(self.name, out)
paul@113 2077
paul@672 2078
        self.indent -= 1
paul@672 2079
        print >>self.out, "}"
paul@672 2080
paul@113 2081
    def start_function(self, name):
paul@159 2082
paul@159 2083
        "Start the function having the given 'name'."
paul@159 2084
paul@113 2085
        self.indent += 1
paul@113 2086
paul@670 2087
        self.start_unit()
paul@670 2088
paul@670 2089
    def end_function(self, name):
paul@670 2090
paul@670 2091
        "End the function having the given 'name'."
paul@670 2092
paul@670 2093
        out = self.end_unit()
paul@670 2094
paul@673 2095
        # Write the signature at the top indentation level.
paul@673 2096
paul@673 2097
        self.indent -= 1
paul@664 2098
        self.write_parameters(name)
paul@113 2099
        print >>self.out, "{"
paul@113 2100
paul@113 2101
        # Obtain local names from parameters.
paul@113 2102
paul@113 2103
        parameters = self.importer.function_parameters[name]
paul@144 2104
        locals = self.importer.function_locals[name].keys()
paul@113 2105
        names = []
paul@670 2106
        volatile_names = []
paul@113 2107
paul@113 2108
        for n in locals:
paul@113 2109
paul@113 2110
            # Filter out special names and parameters. Note that self is a local
paul@113 2111
            # regardless of whether it originally appeared in the parameters or
paul@113 2112
            # not.
paul@113 2113
paul@113 2114
            if n.startswith("$l") or n in parameters or n == "self":
paul@113 2115
                continue
paul@670 2116
            if n in self.volatile_locals:
paul@670 2117
                volatile_names.append(encode_path(n))
paul@670 2118
            else:
paul@670 2119
                names.append(encode_path(n))
paul@113 2120
paul@673 2121
        # Emit required local names at the function indentation level.
paul@673 2122
paul@673 2123
        self.indent += 1
paul@113 2124
paul@113 2125
        if names:
paul@113 2126
            names.sort()
paul@113 2127
            self.writeline("__attr %s;" % ", ".join(names))
paul@113 2128
paul@670 2129
        if volatile_names:
paul@670 2130
            volatile_names.sort()
paul@670 2131
            self.writeline("volatile __attr %s;" % ", ".join(volatile_names))
paul@670 2132
paul@670 2133
        self.flush_unit(name, out)
paul@672 2134
paul@672 2135
        self.indent -= 1
paul@672 2136
        print >>self.out, "}"
paul@144 2137
        print >>self.out
paul@144 2138
paul@664 2139
    def write_parameters(self, name):
paul@664 2140
paul@664 2141
        """
paul@664 2142
        For the function having the given 'name', write definitions of
paul@664 2143
        parameters found in the arguments array.
paul@664 2144
        """
paul@664 2145
paul@664 2146
        # Generate any self reference.
paul@664 2147
paul@664 2148
        l = []
paul@664 2149
paul@664 2150
        if self.is_method(name):
paul@664 2151
            l.append("__attr self")
paul@664 2152
        else:
paul@664 2153
            l.append("__attr __self")
paul@664 2154
paul@664 2155
        # Generate aliases for the parameters.
paul@664 2156
paul@664 2157
        for parameter in self.importer.function_parameters[name]:
paul@673 2158
            l.append("%s__attr %s" % (
paul@673 2159
                parameter in self.volatile_locals and "volatile " or "",
paul@673 2160
                encode_path(parameter)))
paul@664 2161
paul@664 2162
        self.writeline("__attr %s(%s)" % (
paul@664 2163
            encode_function_pointer(name), ", ".join(l)))
paul@664 2164
paul@482 2165
    def write_temporaries(self, name):
paul@482 2166
paul@482 2167
        "Write temporary storage employed by 'name'."
paul@482 2168
paul@858 2169
        # Provide space for the recorded number of temporary variables.
paul@591 2170
paul@482 2171
        if self.uses_temp(name, "__tmp_targets"):
paul@858 2172
            self.writeline("__attr __tmp_targets[%d];" % self.max_function_target)
paul@828 2173
paul@591 2174
        if self.uses_temp(name, "__tmp_contexts"):
paul@858 2175
            self.writeline("__attr __tmp_contexts[%d];" % self.max_context_index)
paul@858 2176
paul@858 2177
        if self.uses_temp(name, "__tmp_values"):
paul@858 2178
            self.writeline("__attr __tmp_values[%d];" % self.max_accessor_index)
paul@482 2179
paul@482 2180
        # Add temporary variable usage details.
paul@482 2181
paul@592 2182
        if self.uses_temp(name, "__tmp_private_context"):
paul@757 2183
            self.writeline("__attr __tmp_private_context;")
paul@482 2184
        if self.uses_temp(name, "__tmp_target_value"):
paul@757 2185
            self.writeline("__attr __tmp_target_value;")
paul@482 2186
        if self.uses_temp(name, "__tmp_result"):
paul@482 2187
            self.writeline("__attr __tmp_result;")
paul@479 2188
paul@479 2189
        module = self.importer.get_module(self.name)
paul@482 2190
paul@482 2191
        if name in module.exception_namespaces:
paul@479 2192
            self.writeline("__exc __tmp_exc;")
paul@149 2193
paul@631 2194
    def start_if(self, first, test_ref):
paul@629 2195
        statement = "%sif" % (not first and "else " or "")
paul@629 2196
paul@629 2197
        # Consume logical results directly.
paul@629 2198
paul@629 2199
        if isinstance(test_ref, LogicalResult):
paul@631 2200
            self.writeline("%s %s" % (statement, test_ref.apply_test()))
paul@637 2201
            temps = test_ref.discards_temporary()
paul@637 2202
            if temps:
paul@637 2203
                self.remove_temps(temps)
paul@629 2204
        else:
paul@631 2205
            self.writeline("%s (__BOOL(%s))" % (statement, test_ref))
paul@629 2206
paul@113 2207
        self.writeline("{")
paul@113 2208
        self.indent += 1
paul@113 2209
paul@113 2210
    def end_if(self):
paul@113 2211
        self.indent -= 1
paul@113 2212
        self.writeline("}")
paul@113 2213
paul@113 2214
    def start_else(self):
paul@113 2215
        self.writeline("else")
paul@113 2216
        self.writeline("{")
paul@113 2217
        self.indent += 1
paul@113 2218
paul@113 2219
    def end_else(self):
paul@113 2220
        self.indent -= 1
paul@113 2221
        self.writeline("}")
paul@113 2222
paul@113 2223
    def statement(self, expr):
paul@113 2224
        s = str(expr)
paul@113 2225
        if s:
paul@128 2226
            self.writestmt("%s;" % s)
paul@113 2227
paul@113 2228
    def statements(self, results):
paul@113 2229
        for result in results:
paul@113 2230
            self.statement(result)
paul@113 2231
paul@159 2232
    def writeline(self, s):
paul@159 2233
        print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1))
paul@159 2234
paul@159 2235
    def writestmt(self, s):
paul@159 2236
        self.writeline(s)
paul@159 2237
paul@159 2238
    def write_comment(self, s):
paul@159 2239
        self.writestmt("/* %s */" % s)
paul@159 2240
paul@113 2241
    def pad(self, extra=0):
paul@113 2242
        return (self.indent + extra) * self.tabstop
paul@113 2243
paul@113 2244
    def indenttext(self, s, levels):
paul@116 2245
        lines = s.split("\n")
paul@116 2246
        out = [lines[0]]
paul@116 2247
        for line in lines[1:]:
paul@116 2248
            out.append(levels * self.tabstop + line)
paul@116 2249
            if line.endswith("("):
paul@116 2250
                levels += 1
paul@122 2251
            elif line.startswith(")"):
paul@116 2252
                levels -= 1
paul@116 2253
        return "\n".join(out)
paul@113 2254
paul@113 2255
# vim: tabstop=4 expandtab shiftwidth=4