Usually in computer science, "primitive" refers to numeric data types. In python, ints and floats are treated as objects, so at the high level there is no sense that they are "primitive." The idea is that
makes a an int object. The object has an _add_ method so that
is simply the call
This is a built in function that returns a new integer object representing the primitive value 9.
In this case, "built in" does not mean that it is in a standard library. Instead, it is implemented in the interpreter (usually this means it is a C function).
In general, at the MIPS level, "a" is associated (by a symbol table) with a memory address, referencing a 12-byte block in the heap. The first 4 bytes store the current type of a (it is the memory address of the appropriate type object). I reserve the next 4 bytes just for use by "primitive" objects. The remaining 4 bytes point to the first element in the object's symbol table. The symbol table contains an element for each instance-specific field/attribute. A symbol table element is 12 bytes: it uses the first 4 bytes to point to the null-terminated string that matches its name, the next 4 bytes to point to the value of object.attribute (which is another object), and the final 4 bytes to point to the next symbol table element (as a linked list).
If I wanted to store the primitive value 5 in the object a as a field, such as a.primitive = 5, then I run into a problem of circularity. To maintain consistency, I require that the value of each field be a pointer to an object. This is a problem because the object representing "5" is a itself. If a.primitive == a, then the object does not actually contain the primitive value. The best solution we found was to include the extra slot in the object format that contains primitive values. Primitives themselves are never used in Python, so it is okay that the Python programmer has no access (either read or write) to the primitive slot. It is only used in the built in methods like _add_, which are hand-written MIPS routines, where it is easy to access the primitive slot.
Currently I am only looking at 4-byte integers, so I will store the primitive value directly in the slot. When I extend it to handle arbitrarily large integers, I will need to change the primitive value slot to point to an array in memory that contains the integer.
Repeated immutable objects
Immutable objects, particularly primitives and strings, are often created and cast aside, only to be needed again later. For example, the code
could create int(4), int(5), int(9), int(5), and int(4), for a total of five integer objects. This is wasteful because the int(4) and int(5) objects should be reused.
Right now, I am ignoring the inefficiency of recreating integers. Since there are only two possible boolean values, though, I have created and labelled a True and False object that I reuse. Since they are immutable, there is no danger of them ever changing.
Searching for attributes: findAttr
In the simplest case, I handle a.field by searching a's symbol table sequentially. If it is not present, then I currently go search type(a)'s symbol table. This may not be the correct search method. After all, the following shows that an instance does not contain all the fields of its type:
One solution is to copy all fields into every object, but this seems COMPLETELY redundant. It does look like class objects (objects with <type type>) may not have the simple dictionary symbol tables that instance objects have.
If I find an unbound method in an object's type's symbol table then I create a new bound method object binding the unbound method to the calling object. These are immutable, so it works that I create a new bound method each time it is called.