micropython

Annotated rsvp.py

225:be4035baf424
2009-05-24 Paul Boddie Fixed direct invocation locations by recording the body block on each function.
paul@68 1
#!/usr/bin/env python
paul@68 2
paul@68 3
"""
paul@68 4
A really simple virtual processor employing a simple set of instructions which
paul@68 5
ignore low-level operations and merely concentrate on variable access, structure
paul@68 6
access, structure allocation and function invocations.
paul@68 7
paul@180 8
Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>
paul@68 9
paul@68 10
This program is free software; you can redistribute it and/or modify it under
paul@68 11
the terms of the GNU General Public License as published by the Free Software
paul@68 12
Foundation; either version 3 of the License, or (at your option) any later
paul@68 13
version.
paul@68 14
paul@68 15
This program is distributed in the hope that it will be useful, but WITHOUT
paul@68 16
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@68 17
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@68 18
details.
paul@68 19
paul@68 20
You should have received a copy of the GNU General Public License along with
paul@68 21
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@68 22
paul@68 23
--------
paul@68 24
paul@68 25
The execution model of the virtual processor involves the following things:
paul@68 26
paul@145 27
  * Memory                          contains constants, global variable
paul@145 28
                                    references and program code
paul@145 29
paul@145 30
  * PC (program counter) stack      contains the return address associated
paul@145 31
                                    with each function invocation
paul@145 32
paul@145 33
  * Frame stack                     contains invocation frames in use and in
paul@145 34
                                    preparation plus temporary storage
paul@145 35
paul@145 36
  * Local frame pointer stack       refers to the frames in the frame stack
paul@145 37
paul@117 38
  * Invocation frame pointer stack
paul@145 39
paul@117 40
  * Exception handler stack
paul@68 41
paul@145 42
  * Exception handler locals stack  refers to the state of the local frame
paul@145 43
                                    pointer stack
paul@145 44
paul@145 45
  * Exception handler PC stack      refers to the state of the PC stack
paul@68 46
paul@145 47
  * Registers: current value,
paul@145 48
               boolean status value,
paul@145 49
               source value,
paul@145 50
               current result,
paul@145 51
               current exception,
paul@145 52
               current callable
paul@68 53
"""
paul@68 54
paul@201 55
from micropython.program import DataObject # for creating "nice" new objects
paul@182 56
paul@68 57
class IllegalInstruction(Exception):
paul@68 58
    pass
paul@68 59
paul@68 60
class IllegalAddress(Exception):
paul@180 61
    def __init__(self, address):
paul@180 62
        self.address = address
paul@180 63
    def __repr__(self):
paul@180 64
        return "IllegalAddress(%r)" % self.address
paul@180 65
    def __str__(self):
paul@180 66
        return repr(self)
paul@68 67
paul@68 68
class EmptyPCStack(Exception):
paul@68 69
    pass
paul@68 70
paul@92 71
class EmptyFrameStack(Exception):
paul@68 72
    pass
paul@68 73
paul@194 74
class BreakpointReached(Exception):
paul@194 75
    pass
paul@194 76
paul@68 77
class RSVPMachine:
paul@68 78
paul@68 79
    "A really simple virtual processor."
paul@68 80
paul@196 81
    def __init__(self, memory, objlist, paramlist, true_constant, false_constant, pc=None, debug=0):
paul@68 82
paul@68 83
        """
paul@68 84
        Initialise the processor with a 'memory' (a list of values containing
paul@196 85
        instructions and data), the object and parameter lists 'objlist' and
paul@196 86
        'paramlist', the addresses 'true_constant' and 'false_constant', and the
paul@196 87
        optional program counter 'pc'.
paul@68 88
        """
paul@68 89
paul@68 90
        self.memory = memory
paul@202 91
        self._objlist = objlist
paul@202 92
        self._paramlist = paramlist
paul@181 93
        self.objlist = objlist.as_raw()
paul@181 94
        self.paramlist = paramlist.as_raw()
paul@196 95
        self.true_constant = true_constant
paul@196 96
        self.false_constant = false_constant
paul@196 97
paul@68 98
        self.pc = pc or 0
paul@68 99
        self.debug = debug
paul@117 100
paul@117 101
        # Stacks.
paul@117 102
paul@68 103
        self.pc_stack = []
paul@68 104
        self.frame_stack = []
paul@143 105
        self.local_sp_stack = [0]
paul@117 106
        self.invocation_sp_stack = []
paul@145 107
        self.handler_stack = [len(self.memory) - 1] # final handler is the end of the code
paul@145 108
        self.handler_local_sp_stack = []
paul@145 109
        self.handler_pc_stack = []
paul@117 110
paul@117 111
        # Registers.
paul@117 112
paul@119 113
        self.instruction = None
paul@119 114
        self.operand = None
paul@117 115
        self.value = None
paul@117 116
        self.status = None
paul@117 117
        self.source = None
paul@132 118
        self.callable = None
paul@117 119
        self.result = None
paul@117 120
        self.exception = None
paul@68 121
paul@146 122
        # Constants.
paul@146 123
paul@221 124
        cls = self._get_class("__builtins__", "AttributeError")
paul@221 125
        self.attr_error = cls.location
paul@221 126
        self.attr_error_instance = cls.instance_template_location
paul@221 127
        cls = self._get_class("__builtins__", "TypeError")
paul@221 128
        self.type_error = cls.location
paul@221 129
        self.type_error_instance = cls.instance_template_location
paul@221 130
        cls = self._get_class("__builtins__", "IndexError")
paul@221 131
        self.index_error = cls.location
paul@221 132
        self.index_error_instance = cls.instance_template_location
paul@181 133
paul@181 134
        # Native class constants.
paul@181 135
paul@221 136
        cls = self._get_class("__builtins__", "int")
paul@221 137
        self.int_class = cls.location
paul@221 138
        self.int_instance = cls.instance_template_location
paul@221 139
        cls = self._get_class("__builtins__", "list")
paul@221 140
        self.list_instance = cls.instance_template_location
paul@146 141
paul@194 142
        # Debugging attributes.
paul@194 143
paul@194 144
        self.breakpoints = set()
paul@194 145
paul@221 146
    def _get_class(self, module, name):
paul@221 147
        return self._objlist.access(module, name).get_value()
paul@221 148
paul@162 149
    # Debugging methods.
paul@162 150
paul@137 151
    def dump(self):
paul@138 152
        print "PC", self.pc, "->", self.load(self.pc)
paul@137 153
        print "PC stack", self.pc_stack
paul@137 154
        print "Frame stack", self.frame_stack
paul@137 155
        print "Local stack pointers", self.local_sp_stack
paul@137 156
        print "Invocation stack pointers", self.invocation_sp_stack
paul@137 157
        print "Handler stack", self.handler_stack
paul@145 158
        print "Handler frame stack", self.handler_local_sp_stack
paul@145 159
        print "Handler PC stack", self.handler_pc_stack
paul@137 160
        print
paul@137 161
        print "Instruction", self.instruction
paul@137 162
        print "Operand", self.operand
paul@137 163
        print "Value", self.value
paul@137 164
        print "Status", self.status
paul@137 165
        print "Source", self.source
paul@137 166
        print "Callable", self.callable
paul@137 167
        print "Result", self.result
paul@137 168
        print "Exception", self.exception
paul@137 169
paul@162 170
    def show(self):
paul@162 171
        self.show_memory(self.memory, 0)
paul@162 172
paul@162 173
    def show_pc(self, run_in=10):
paul@162 174
        start = max(0, self.pc - run_in)
paul@162 175
        end = self.pc + run_in
paul@162 176
        memory = self.memory[start:end]
paul@162 177
        self.show_memory(memory, start)
paul@162 178
paul@162 179
    def show_memory(self, memory, start):
paul@162 180
        for i, x in enumerate(memory):
paul@162 181
            location = start + i
paul@162 182
            if location == self.pc:
paul@162 183
                print "->",
paul@162 184
            else:
paul@162 185
                print "  ",
paul@162 186
            print "%5d %r" % (location, x)
paul@162 187
paul@162 188
    def step(self, dump=0):
paul@138 189
        self.execute()
paul@162 190
        self.show_pc()
paul@162 191
        if dump:
paul@162 192
            self.dump()
paul@162 193
paul@194 194
    def set_break(self, location):
paul@194 195
        self.breakpoints.add(location)
paul@194 196
paul@162 197
    # Internal operations.
paul@138 198
paul@68 199
    def load(self, address):
paul@68 200
paul@68 201
        "Return the value at the given 'address'."
paul@68 202
paul@68 203
        try:
paul@68 204
            return self.memory[address]
paul@68 205
        except IndexError:
paul@180 206
            raise IllegalAddress(address)
paul@180 207
        except TypeError:
paul@180 208
            raise IllegalAddress(address)
paul@68 209
paul@68 210
    def save(self, address, value):
paul@68 211
paul@68 212
        "Save to the given 'address' the specified 'value'."
paul@68 213
paul@68 214
        try:
paul@68 215
            self.memory[address] = value
paul@68 216
        except IndexError:
paul@180 217
            raise IllegalAddress(address)
paul@180 218
        except TypeError:
paul@180 219
            raise IllegalAddress(address)
paul@68 220
paul@68 221
    def new(self, size):
paul@68 222
paul@68 223
        """
paul@68 224
        Allocate space of the given 'size', returning the address of the space.
paul@68 225
        """
paul@68 226
paul@68 227
        addr = len(self.memory)
paul@68 228
        for i in range(0, size):
paul@68 229
            self.memory.append(None)
paul@68 230
        return addr
paul@68 231
paul@68 232
    def push_pc(self, data):
paul@68 233
paul@68 234
        "Push 'data' onto the PC stack."
paul@68 235
paul@68 236
        self.pc_stack.append(data)
paul@68 237
paul@68 238
    def pull_pc(self):
paul@68 239
paul@68 240
        "Pull a value from the PC stack and return it."
paul@68 241
paul@68 242
        try:
paul@68 243
            return self.pc_stack.pop()
paul@68 244
        except IndexError:
paul@68 245
            raise EmptyPCStack
paul@68 246
paul@90 247
    def run(self):
paul@68 248
paul@68 249
        "Execute code in the memory, starting from the current PC address."
paul@68 250
paul@68 251
        try:
paul@68 252
            while 1:
paul@90 253
                self.execute()
paul@68 254
        except EmptyPCStack:
paul@68 255
            pass
paul@68 256
paul@221 257
        print "Execution terminated",
paul@221 258
        if self.exception is not None:
paul@222 259
            ref = self.exception
paul@222 260
            addr = self.load(ref + 1)
paul@221 261
            print "with exception:", self.load(ref)
paul@222 262
            print "At address %d: %r" % (addr, self.load(addr))
paul@221 263
        else:
paul@221 264
            print "successfully."
paul@221 265
paul@90 266
    def execute(self):
paul@90 267
paul@90 268
        "Execute code in the memory at the current PC address."
paul@90 269
paul@194 270
        if self.pc in self.breakpoints:
paul@194 271
            self.breakpoints.remove(self.pc)
paul@194 272
            raise BreakpointReached
paul@194 273
paul@119 274
        self.instruction = self.load(self.pc)
paul@118 275
paul@118 276
        # Process any inputs of the instruction.
paul@118 277
paul@119 278
        self.process_inputs()
paul@137 279
paul@137 280
        # Perform the instruction itself.
paul@137 281
paul@137 282
        next_pc = self.perform(self.instruction)
paul@118 283
paul@119 284
        # Update the program counter.
paul@118 285
paul@119 286
        if next_pc is None:
paul@119 287
            self.pc += 1
paul@119 288
        else:
paul@119 289
            self.pc = next_pc
paul@118 290
paul@137 291
    def get_method(self, instruction):
paul@137 292
paul@137 293
        "Return the handler method for the given 'instruction'."
paul@119 294
paul@137 295
        instruction_name = instruction.__class__.__name__
paul@137 296
        if self.debug:
paul@137 297
            print "%8d %s" % (self.pc, instruction_name)
paul@119 298
        method = getattr(self, instruction_name, None)
paul@90 299
        if method is None:
paul@119 300
            raise IllegalInstruction, (self.pc, instruction_name)
paul@118 301
        return method
paul@118 302
paul@137 303
    def perform(self, instruction):
paul@137 304
paul@137 305
        "Perform the 'instruction', returning the next PC value or None."
paul@137 306
paul@137 307
        self.operand = instruction.get_operand()
paul@137 308
        method = self.get_method(instruction)
paul@137 309
        return method()
paul@137 310
paul@119 311
    def process_inputs(self):
paul@118 312
paul@118 313
        """
paul@119 314
        Process any inputs of the current instruction. This permits any directly
paul@118 315
        connected sub-instructions to produce the effects that separate
paul@118 316
        instructions would otherwise have.
paul@118 317
        """
paul@118 318
paul@190 319
        value = self.value
paul@174 320
        if self.instruction.source is not None:
paul@174 321
            self.perform(self.instruction.source)
paul@174 322
            self.source = self.value
paul@190 323
        self.value = value
paul@174 324
        if self.instruction.input is not None:
paul@174 325
            self.perform(self.instruction.input)
paul@90 326
paul@68 327
    def jump(self, addr, next):
paul@68 328
paul@68 329
        """
paul@68 330
        Jump to the subroutine at (or identified by) 'addr'. If 'addr'
paul@68 331
        identifies a library function then invoke the library function and set
paul@68 332
        PC to 'next' afterwards; otherwise, set PC to 'addr'.
paul@68 333
        """
paul@68 334
paul@181 335
        # Trap library functions introduced through the use of strings instead
paul@181 336
        # of proper locations.
paul@181 337
paul@68 338
        if isinstance(addr, str):
paul@196 339
            handler = self.native_functions[addr](self)
paul@196 340
            if handler is None:
paul@196 341
                return next
paul@196 342
            else:
paul@196 343
                return handler
paul@68 344
        else:
paul@138 345
            self.push_pc(self.pc + 1)
paul@119 346
            return addr
paul@68 347
paul@68 348
    # Instructions.
paul@68 349
paul@98 350
    def LoadConst(self):
paul@223 351
        self.value = self.operand, self.operand
paul@223 352
paul@223 353
    def LoadFunction(self):
paul@198 354
        self.value = None, self.operand # context of constant is not interesting
paul@68 355
paul@68 356
    def LoadName(self):
paul@117 357
        frame = self.local_sp_stack[-1]
paul@119 358
        self.value = self.frame_stack[frame + self.operand]
paul@68 359
paul@68 360
    def StoreName(self):
paul@117 361
        frame = self.local_sp_stack[-1]
paul@202 362
        self.frame_stack[frame + self.operand] = self.source
paul@68 363
paul@72 364
    LoadTemp = LoadName
paul@203 365
paul@203 366
    def StoreTemp(self):
paul@203 367
        frame = self.local_sp_stack[-1]
paul@203 368
        self.frame_stack[frame + self.operand] = self.value
paul@72 369
paul@72 370
    def LoadAddress(self):
paul@119 371
        # Preserve context (potentially null).
paul@119 372
        self.value = self.load(self.operand)
paul@72 373
paul@117 374
    def LoadAddressContext(self):
paul@191 375
        context, ref = self.load(self.operand)
paul@191 376
        inst_context, inst_ref = self.value
paul@194 377
        self.value = inst_ref, ref
paul@194 378
paul@194 379
    def LoadAddressContextCond(self):
paul@194 380
        context, ref = self.load(self.operand)
paul@194 381
        inst_context, inst_ref = self.value
paul@223 382
        self.value = self._LoadAddressContextCond(context, ref, inst_ref)
paul@72 383
paul@72 384
    def StoreAddress(self):
paul@119 385
        # Preserve context.
paul@190 386
        self.save(self.operand, self.source)
paul@72 387
paul@223 388
    def StoreAddressContext(self):
paul@223 389
        # Overwrite context if null.
paul@223 390
        context_context, context_ref = self.value
paul@223 391
        source_context, source_ref = self.source
paul@223 392
        if source_context is None:
paul@223 393
            context = context_ref
paul@223 394
        else:
paul@223 395
            context = source_context
paul@223 396
        self.save(self.operand, (context, source_ref))
paul@223 397
paul@117 398
    def MakeObject(self):
paul@138 399
        size = self.operand
paul@138 400
        context, ref = self.value
paul@184 401
        # NOTE: Referencing the instance template.
paul@184 402
        addr = self._MakeObject(size, ref - 1)
paul@184 403
        # Introduce object as context for the new object.
paul@185 404
        self.value = addr, addr
paul@72 405
paul@118 406
    def LoadAttr(self):
paul@118 407
        context, ref = self.value
paul@127 408
        # Retrieved context should already be appropriate for the instance.
paul@184 409
        # NOTE: Adding 1 to skip any header.
paul@174 410
        self.value = self.load(ref + self.operand + 1)
paul@118 411
paul@118 412
    def StoreAttr(self):
paul@119 413
        context, ref = self.value
paul@129 414
        # Target should already be an instance.
paul@184 415
        # NOTE: Adding 1 to skip any header.
paul@174 416
        self.save(ref + self.operand + 1, self.source)
paul@119 417
paul@119 418
    def LoadAttrIndex(self):
paul@118 419
        context, ref = self.value
paul@182 420
        data = self.load(ref)
paul@182 421
        element = self.objlist[data.classcode + self.operand]
paul@194 422
        attr_index, class_attr, offset = element
paul@135 423
        if attr_index == self.operand:
paul@127 424
            if class_attr:
paul@194 425
                self.value = self.load(offset) # offset is address of class attribute
paul@127 426
            else:
paul@127 427
                self.value = self.load(ref + offset)
paul@119 428
        else:
paul@222 429
            self.exception = self._MakeObject(2, self.attr_error_instance)
paul@190 430
            return self.RaiseException()
paul@118 431
paul@194 432
    def LoadAttrIndexContext(self):
paul@194 433
        context, ref = self.value
paul@194 434
        data = self.load(ref)
paul@194 435
        element = self.objlist[data.classcode + self.operand]
paul@194 436
        attr_index, class_attr, offset = element
paul@194 437
        if attr_index == self.operand:
paul@194 438
            loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute
paul@194 439
            self.value = ref, loaded_ref
paul@194 440
        else:
paul@222 441
            self.exception = self._MakeObject(2, self.attr_error_instance)
paul@194 442
            return self.RaiseException()
paul@194 443
paul@194 444
    def LoadAttrIndexContextCond(self):
paul@194 445
        context, ref = self.value
paul@194 446
        data = self.load(ref)
paul@194 447
        element = self.objlist[data.classcode + self.operand]
paul@194 448
        attr_index, class_attr, offset = element
paul@194 449
        if attr_index == self.operand:
paul@194 450
            if class_attr:
paul@194 451
                loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute
paul@223 452
                if data.attrcode is None: # absent attrcode == class
paul@223 453
                    self.value = loaded_context, loaded_ref
paul@223 454
                else:
paul@223 455
                    self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, ref)
paul@194 456
            else:
paul@194 457
                self.value = self.load(ref + offset)
paul@194 458
        else:
paul@222 459
            self.exception = self._MakeObject(2, self.attr_error_instance)
paul@194 460
            return self.RaiseException()
paul@194 461
paul@129 462
    def StoreAttrIndex(self):
paul@129 463
        context, ref = self.value
paul@182 464
        data = self.load(ref)
paul@182 465
        element = self.objlist[data.classcode + self.operand]
paul@194 466
        attr_index, class_attr, offset = element
paul@135 467
        if attr_index == self.operand:
paul@129 468
            if class_attr:
paul@222 469
                self.exception = self._MakeObject(2, self.type_error_instance)
paul@190 470
                return self.RaiseException()
paul@129 471
            else:
paul@129 472
                self.save(ref + offset, self.source)
paul@129 473
        else:
paul@222 474
            self.exception = self._MakeObject(2, self.attr_error_instance)
paul@190 475
            return self.RaiseException()
paul@118 476
paul@127 477
    # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden.
paul@127 478
paul@98 479
    def MakeFrame(self):
paul@117 480
        self.invocation_sp_stack.append(len(self.frame_stack))
paul@119 481
        self.frame_stack.extend([None] * self.operand)
paul@98 482
paul@98 483
    def DropFrame(self):
paul@117 484
        self.local_sp_stack.pop()
paul@117 485
        frame = self.invocation_sp_stack.pop()
paul@117 486
        self.frame_stack = self.frame_stack[:frame] # reset stack before call
paul@98 487
paul@137 488
    def RecoverFrame(self):
paul@137 489
        self.local_sp_stack.pop()
paul@137 490
paul@129 491
    def StoreFrame(self):
paul@129 492
        frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame
paul@129 493
        self.frame_stack[frame + self.operand] = self.value
paul@129 494
paul@129 495
    def StoreFrameIndex(self):
paul@184 496
        context, ref = self.value
paul@129 497
        frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame
paul@182 498
        data = self.load(ref)
paul@190 499
        element = self.paramlist[data.funccode + self.operand]
paul@190 500
        # NOTE: Need to ensure correct positioning where a context has been generated.
paul@190 501
        param_index, offset = element
paul@190 502
        if param_index == self.operand:
paul@190 503
            self.frame_stack[frame + offset + 1] = self.source # add 1 to skip the context always generated
paul@129 504
        else:
paul@222 505
            self.exception = self._MakeObject(2, self.type_error_instance)
paul@190 506
            return self.RaiseException()
paul@129 507
paul@132 508
    def LoadCallable(self):
paul@132 509
        context, ref = self.value
paul@182 510
        data = self.load(ref)
paul@219 511
        self.callable = data.codeaddr
paul@132 512
paul@132 513
    def StoreCallable(self):
paul@132 514
        context, ref = self.value
paul@134 515
        # NOTE: Should improve the representation and permit direct saving.
paul@182 516
        data = self.load(ref)
paul@207 517
        self.save(ref, (data.classcode, data.attrcode) + self.callable)
paul@132 518
paul@132 519
    def LoadContext(self):
paul@132 520
        context, ref = self.value
paul@218 521
        # NOTE: Omission of the context of the context would make things like
paul@218 522
        # NOTE: self() inside methods impossible.
paul@218 523
        self.value = context, context
paul@132 524
paul@222 525
    def CheckContext(self):
paul@222 526
        self.status = self.value[1] is not None
paul@222 527
paul@222 528
    def CheckClassContext(self):
paul@222 529
        context_context, context_ref = self.value
paul@222 530
        context_data = self.load(context_ref)
paul@222 531
paul@222 532
        # Classes are not themselves usable as the self argument.
paul@222 533
        # NOTE: This may change at some point.
paul@222 534
        # However, where classes appear as the context, instance
paul@222 535
        # compatibility is required in the first argument.
paul@222 536
paul@222 537
        self.status = context_data.attrcode is None # absent attrcode == class
paul@222 538
paul@132 539
    def CheckFrame(self):
paul@215 540
        (nargs, ndefaults, has_star) = self.operand
paul@215 541
paul@215 542
        # The frame is actually installed as the locals.
paul@215 543
        # Retrieve the context from the first local.
paul@215 544
paul@215 545
        frame = self.local_sp_stack[-1]
paul@215 546
        nlocals = len(self.frame_stack[frame:])
paul@134 547
paul@215 548
        if not ((nargs - ndefaults) <= nlocals and (nlocals <= nargs or has_star)):
paul@221 549
            #raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs)
paul@222 550
            self.exception = self._MakeObject(2, self.type_error_instance)
paul@221 551
            return self.RaiseException()
paul@215 552
paul@215 553
    def FillDefaults(self):
paul@218 554
        # NOTE: Make the get_operand method of the instruction provide the
paul@218 555
        # NOTE: function location.
paul@218 556
        (nargs, ndefaults, fn) = self.operand
paul@215 557
paul@215 558
        # The frame is actually installed as the locals.
paul@215 559
paul@215 560
        frame = self.local_sp_stack[-1]
paul@215 561
        nlocals = len(self.frame_stack[frame:])
paul@134 562
paul@190 563
        # Support population of defaults.
paul@190 564
        # This involves copying the "attributes" of a function into the frame.
paul@190 565
paul@215 566
        default = nlocals - (nargs - ndefaults)
paul@215 567
        self.frame_stack.extend([None] * (nargs - nlocals))
paul@215 568
        pos = nlocals
paul@218 569
        ref = fn.location
paul@190 570
paul@215 571
        while pos < nargs:
paul@190 572
            self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header
paul@190 573
            default += 1
paul@190 574
            pos += 1
paul@117 575
paul@134 576
    def CheckSelf(self):
paul@134 577
        context, ref = self.value
paul@134 578
        target_context, target_ref = self.source
paul@135 579
paul@145 580
        # Check the details of the proposed context and the target's context.
paul@135 581
paul@181 582
        self.status = self._CheckInstance(ref, target_context)
paul@124 583
paul@132 584
    def JumpWithFrame(self):
paul@219 585
        codeaddr = self.callable
paul@132 586
        self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
paul@138 587
        return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one
paul@98 588
paul@215 589
    def JumpWithFrameDirect(self):
paul@215 590
        operand = self.operand
paul@215 591
        self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
paul@215 592
        return self.jump(operand, self.pc + 1) # return to the instruction after this one
paul@215 593
paul@132 594
    def ExtendFrame(self):
paul@138 595
        self.frame_stack.extend([None] * self.operand)
paul@98 596
paul@137 597
    def AdjustFrame(self):
paul@208 598
        self.invocation_sp_stack[-1] += self.operand
paul@137 599
paul@68 600
    def Return(self):
paul@138 601
        return self.pull_pc()
paul@68 602
paul@117 603
    def LoadResult(self):
paul@117 604
        self.value = self.result
paul@117 605
paul@117 606
    def StoreResult(self):
paul@117 607
        self.result = self.value
paul@117 608
paul@118 609
    def Jump(self):
paul@119 610
        return self.operand
paul@98 611
paul@68 612
    def JumpIfTrue(self):
paul@117 613
        if self.status:
paul@119 614
            return self.operand
paul@68 615
paul@68 616
    def JumpIfFalse(self):
paul@117 617
        if not self.status:
paul@119 618
            return self.operand
paul@68 619
paul@129 620
    def LoadException(self):
paul@197 621
        self.value = self.exception, self.exception
paul@129 622
paul@129 623
    def StoreException(self):
paul@145 624
        self.exception = self.value[1]
paul@129 625
paul@129 626
    def RaiseException(self):
paul@221 627
        # NOTE: Adding the program counter as the first attribute.
paul@222 628
        self.save(self.exception + 1, self.pc)
paul@221 629
        # Jumping to the current handler.
paul@145 630
        return self.handler_stack[-1]
paul@129 631
paul@129 632
    def PushHandler(self):
paul@129 633
        self.handler_stack.append(self.operand)
paul@145 634
        self.handler_local_sp_stack.append(len(self.local_sp_stack))
paul@145 635
        self.handler_pc_stack.append(len(self.pc_stack))
paul@129 636
paul@129 637
    def PopHandler(self):
paul@145 638
        # Reduce the local frame pointer stack to refer to the handler's frame.
paul@145 639
        self.local_sp_stack = self.local_sp_stack[:self.handler_local_sp_stack.pop()]
paul@145 640
        # Reduce the PC stack to discard all superfluous return addresses.
paul@145 641
        self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()]
paul@129 642
        self.handler_stack.pop()
paul@129 643
paul@129 644
    def CheckException(self):
paul@181 645
        self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1])
paul@129 646
paul@129 647
    def TestIdentity(self):
paul@145 648
        self.status = self.value[1] == self.source[1]
paul@129 649
paul@129 650
    def TestIdentityAddress(self):
paul@129 651
        self.status = self.value[1] == self.operand
paul@129 652
paul@129 653
    # LoadBoolean is implemented in the generated code.
paul@129 654
    # StoreBoolean is implemented by testing against the True value.
paul@68 655
paul@132 656
    def InvertBoolean(self):
paul@132 657
        self.status = not self.status
paul@132 658
paul@145 659
    # Common implementation details.
paul@145 660
paul@145 661
    def _CheckInstance(self, ref, cls):
paul@182 662
        data = self.load(ref)
paul@182 663
        target_data = self.load(cls)
paul@145 664
paul@223 665
        # Insist on instance vs. class.
paul@223 666
paul@223 667
        if data.attrcode is None: # absent attrcode == class
paul@223 668
            return 0
paul@191 669
paul@207 670
        if target_data.attrcode is not None: # present attrcode == instance
paul@191 671
            return 0
paul@191 672
paul@145 673
        # Find the table entry for the descendant.
paul@145 674
paul@182 675
        element = self.objlist[target_data.classcode + data.attrcode]
paul@220 676
        if element is not None:
paul@220 677
            attr_index, class_attr, offset = element
paul@220 678
            return attr_index == data.attrcode
paul@220 679
        else:
paul@220 680
            return 0
paul@181 681
paul@181 682
    def _MakeObject(self, size, ref):
paul@203 683
        # Load the template.
paul@182 684
        data = self.load(ref)
paul@181 685
        addr = self.new(size)
paul@203 686
        # Save the header, overriding the size.
paul@203 687
        self.save(addr, data.with_size(size))
paul@181 688
        return addr
paul@181 689
paul@223 690
    def _LoadAddressContextCond(self, context, ref, inst_ref):
paul@191 691
paul@191 692
        # Check the instance context against the target's context.
paul@191 693
paul@191 694
        if self._CheckInstance(inst_ref, context):
paul@191 695
            # Replace the context with the instance.
paul@191 696
            return inst_ref, ref
paul@191 697
        else:
paul@191 698
            return context, ref
paul@191 699
paul@181 700
    # Native function implementations.
paul@181 701
paul@181 702
    def builtins_int_add(self):
paul@181 703
        frame = self.local_sp_stack[-1]
paul@181 704
paul@181 705
        # Get operands addresses.
paul@181 706
paul@181 707
        left_context, left = self.frame_stack[frame]
paul@181 708
        right_context, right = self.frame_stack[frame + 1]
paul@181 709
paul@181 710
        # Test operand suitability.
paul@181 711
paul@221 712
        if not self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class):
paul@222 713
            self.exception = self._MakeObject(2, self.type_error_instance)
paul@181 714
            return self.RaiseException()
paul@181 715
paul@181 716
        # NOTE: Assume single location for data.
paul@181 717
paul@181 718
        left_data = left + 1
paul@181 719
        right_data = right + 1
paul@181 720
paul@181 721
        # Make a new object.
paul@181 722
paul@221 723
        addr = self._MakeObject(2, self.int_instance)
paul@196 724
paul@196 725
        # Store the result.
paul@196 726
        # NOTE: The data is considered ready to use.
paul@196 727
paul@181 728
        self.save(addr + 1, self.load(left_data) + self.load(right_data))
paul@181 729
paul@187 730
        # Return the new object.
paul@187 731
        # Introduce object as context for the new object.
paul@181 732
paul@187 733
        self.result = addr, addr
paul@181 734
paul@196 735
    def builtins_int_bool(self):
paul@196 736
        frame = self.local_sp_stack[-1]
paul@196 737
paul@196 738
        # Get operands addresses.
paul@196 739
paul@196 740
        left_context, left = self.frame_stack[frame]
paul@196 741
paul@196 742
        # Test operand suitability.
paul@196 743
paul@221 744
        if not self._CheckInstance(left, self.int_class):
paul@222 745
            self.exception = self._MakeObject(2, self.type_error_instance)
paul@196 746
            return self.RaiseException()
paul@196 747
paul@196 748
        # NOTE: Assume single location for data.
paul@196 749
paul@196 750
        left_data = left + 1
paul@196 751
paul@196 752
        # Test the data.
paul@196 753
        # NOTE: The data is considered ready to use.
paul@196 754
paul@196 755
        if self.load(left_data) != 0:
paul@196 756
            self.result = self.true_constant, self.true_constant
paul@196 757
        else:
paul@196 758
            self.result = self.false_constant, self.false_constant
paul@196 759
paul@216 760
    def builtins_int_neg(self):
paul@216 761
        frame = self.local_sp_stack[-1]
paul@216 762
paul@216 763
        # Get operands addresses.
paul@216 764
paul@216 765
        left_context, left = self.frame_stack[frame]
paul@216 766
paul@216 767
        # Test operand suitability.
paul@216 768
paul@221 769
        if not self._CheckInstance(left, self.int_class):
paul@222 770
            self.exception = self._MakeObject(2, self.type_error_instance)
paul@216 771
            return self.RaiseException()
paul@216 772
paul@216 773
        # NOTE: Assume single location for data.
paul@216 774
paul@216 775
        left_data = left + 1
paul@216 776
paul@216 777
        # Make a new object.
paul@216 778
paul@221 779
        addr = self._MakeObject(2, self.int_instance)
paul@216 780
paul@216 781
        # Store the result.
paul@216 782
        # NOTE: The data is considered ready to use.
paul@216 783
paul@216 784
        self.save(addr + 1, -self.load(left_data))
paul@216 785
paul@216 786
        # Return the new object.
paul@216 787
        # Introduce object as context for the new object.
paul@216 788
paul@216 789
        self.result = addr, addr
paul@216 790
paul@197 791
    def builtins_bool_bool(self):
paul@197 792
        frame = self.local_sp_stack[-1]
paul@197 793
paul@197 794
        # Get operands addresses.
paul@197 795
paul@197 796
        left_context, left = self.frame_stack[frame]
paul@197 797
        self.result = left, left
paul@197 798
paul@203 799
    def builtins_list_new(self):
paul@203 800
        frame = self.local_sp_stack[-1]
paul@203 801
paul@203 802
        # NOTE: Specific copying of tuples/lists.
paul@203 803
paul@203 804
        args_context, args = self.frame_stack[frame]
paul@203 805
        header = self.load(args)
paul@203 806
paul@221 807
        list = self._MakeObject(header.size, self.list_instance)
paul@203 808
        for i in range(1, header.size):
paul@203 809
            self.save(list + i, self.load(args + i))
paul@203 810
paul@203 811
        self.result = list, list
paul@203 812
paul@216 813
    def builtins_list_getitem(self):
paul@216 814
        frame = self.local_sp_stack[-1]
paul@216 815
paul@216 816
        # Get operands addresses.
paul@216 817
paul@216 818
        obj_context, obj = self.frame_stack[frame]
paul@216 819
        item_context, item = self.frame_stack[frame + 1]
paul@216 820
paul@216 821
        header = self.load(obj)
paul@216 822
        nelements = header.size - 1
paul@216 823
paul@216 824
        # NOTE: Assume single location for data.
paul@216 825
paul@216 826
        item_pos = self.load(item + 1)
paul@216 827
        if item_pos >= 0 and item_pos < nelements:
paul@216 828
            pass
paul@216 829
        elif item_pos < 0 and item_pos >= -nelements:
paul@216 830
            item_pos = nelements + item_pos
paul@216 831
        else:
paul@222 832
            self.exception = self._MakeObject(2, self.index_error_instance)
paul@216 833
            return self.RaiseException()
paul@216 834
paul@216 835
        self.result = self.load(obj + 1 + item_pos)
paul@216 836
paul@208 837
    def builtins_object_init(self):
paul@208 838
        pass
paul@208 839
paul@181 840
    native_functions = {
paul@181 841
        "__builtins__.int.__add__" : builtins_int_add,
paul@196 842
        "__builtins__.int.__bool__" : builtins_int_bool,
paul@216 843
        "__builtins__.int.__neg__" : builtins_int_neg,
paul@197 844
        "__builtins__.bool.__bool__" : builtins_bool_bool,
paul@203 845
        "__builtins__.list" : builtins_list_new,
paul@216 846
        "__builtins__.list.__getitem__" : builtins_list_getitem,
paul@208 847
        "__builtins__.object.__init__" : builtins_object_init,
paul@181 848
        }
paul@145 849
paul@68 850
# vim: tabstop=4 expandtab shiftwidth=4