TweetFollow Us on Twitter

Object Pascal
Volume Number:2
Issue Number:12
Column Tag:Pascal Procedures

Introduction to Object Pascal

By Ken Doyle, Apple Computer, Inc.

Introduction

If you have been reading about MacApp, you may be wondering if you have to have the Macintosh Programmer's Workshop (MPW) and if MacApp programs must be written in Object Pascal. The answer is yes, for now. For those of you who like some of the other Pascal compilers out there, or prefer to program in another language such as C, you are currently out of luck. The reason is two-fold. Most languages for the Macintosh do not support the object-oriented concepts upon which MacApp so heavily relies, and even if they do, they don't use the same run-time scheme that Object Pascal does.

In this article I will first present a description of the syntax of Object Pascal and comment on some of the semantics involved in using the syntax. I will discuss the various degrees of compatibility that another language or compiler needs to achieve in order to make use of MacApp and what steps are necessary in meeting that goal. In particular, the exact format of the generated code and the run-time routines that deal with that code will be shown. I will talk a bit about how we added objects to MPW's Assembly language. Finally I present a scheme for optimizing what we've already learned.

Object Pascal

Object Pascal is an extension to the Pascal language that was developed at Apple in consultation with Niklaus Wirth, the inventor of Pascal. It is descended from an earlier attempt at an object-oriented version of Pascal called Clascal, which was available on the Lisa computer. MacApp itself is descended from the Lisa Toolkit, an application framework for creating Lisa applications. The Lisa Toolkit was written in Clascal.

There are actually very few syntactic additions to Pascal in Object Pascal. A new data type is added, the object. An object is very much like a record in that it can have multiple data fields of arbitrary types. In addition, you can specify a list of procedures and functions, referred to as methods, for a particular object type. These methods define the actions that an object of this type can perform. For example, you could define a Shape object type as follows:

 type 
 Shape = object
 bounds:Rect;
 color: Pattern;
 procedure Draw;
 procedure Erase;
 procedure Rotate(angle: integer);
 procedure Move(delta: Point);
 function Area: integer;
 end;

Furthermore, you can define an object type that inherits the fields and methods of another object type. The new type can define additional fields and methods and can choose to selectively override methods that it has inherited.

 type
 Circle = object(Shape)
 radius: integer;
 procedure Draw; override;
 function Area: integer; override;
 procedure SetRadius(newRadius: integer);
 end;

 var aCircle: Circle;

An object type is often referred to as a class. In the above example, Circle is a subclass of Shape. Shape is the superclass of Circle. A class (object type) can have many subclasses (descendants), but only one superclass (immediate ancestor). When speaking of the relationships conceptually , I will more often use the class terminology. When speaking in terms of Pascal data types I use the object type terms.

Objects are created by calling the Pascal built-in procedure New on a variable of an object type. You say New(aCircle) to create an instance of the object type Circle. The New procedure, when used with an object type variable allocates sufficient storage on the heap for the object and sets the value of the variable to be a handle (pointer to a pointer) to that data. The double arrow normally required for handle dereferencing is done automatically by the compiler, so fields are accessed directly, eg: aCircle.bounds, NOT aCircle^^.bounds. Likewise, to invoke a method you use the same notation: aCircle.Draw invokes the Draw method of the Circle object type, presumably drawing itself on some display. Since all object type variables are actually handles to the data, an assignment such as shape1 := shape2 causes shape1 to point to the same data as shape2.

The fields of an object can themselves be references to other objects. For example you could have a nextShape field in the Shape definition if you wanted to have a linked list of shapes. Object Pascal allows you to specify the type of a field to be a reference to an object type not yet declared. In this manner, you can have circular references of object types to one another. If the compiler encounters an undeclared type identifier, it assumes it is an object type that will be declared later. If the type is not declared later, an error will be reported. The size of the not yet declared object is unimportant since the reference to it is always just a four byte handle.

The depth to which an object type can inherit is unlimited. You could define a descendant of Circle and another descendant of that type and so on. Each succeeding descendant inherits all of the fields and methods of all of its ancestors.

Figure 1.

Object Pascal requires that the object type definitions be at the highest level in a unit or program, always as a type declaration. For a unit this can be either in the interface or the implementation part. The body or actual code for the methods appears in the procedure and function part of the unit or program. If the body of a declared method does not appear in the file, the compiler issues a "method not implemented" error. The body of a method is just like that of any procedure or function:

 procedure Shape.Erase;
 begin
 EraseRect(bounds);
 end;

 procedure Circle.Draw;
 begin
 FillOval(bounds, color);
 FrameOval(bounds);
 end;

There are several things to note in these two examples. The name of the method is given as TypeName.MethodName to distinguish which method is being defined. When inside a method, there is always an implicit parameter called SELF. SELF refers to the object that invoked the method. The fields of the object could be accessed as SELF.bounds or SELF.color, however the compiler provides an implicit "with SELF do" block around the method making the field names directly accessible. Similarly, you could invoke another method from within a method by saying SELF.OtherMethod but again just OtherMethod is sufficient. This obviates the need for SELF other than when one wishes to pass the object itself to another routine, eg: AddMeToList(SELF). For the statement aCircle.Draw, since aCircle is of type Circle, the Circle.Draw method would be called rather than the Shape.Draw method. In addition, if the aCircle.Erase is called, since Circle did not override the Erase method, the Shape.Erase method would be invoked. This is fairly straightforward. Slightly less obvious behavior occurs if the following code is executed:

 var aShape: Shape;
  aCircle: Circle;

 New(aCircle);
 aCircle.bounds := someRect;
 aCircle.color := white;
 aCircle.radius := 60;
 aShape := aCircle;
 aShape.Draw;

When aShape.Draw is executed, which method is called: Shape.Draw or Circle.Draw? Even though aShape is declared as a Shape, the assignment to aCircle causes it to be a Circle object and thus Circle.Draw would be the method called. This is accomplished by means of a two byte type identifier at the beginning of every object (see Figure 1). This raises some important points. The assignment aShape := aCircle is "safe" because any fields or methods accessed for a Shape object will be valid for a Circle object. But the reverse assignment aCircle := aShape is not safe since the additional fields or methods in Circle will not necessarily be understood by a Shape object. For example, if we later tried to invoke aCircle.SetRadius it would not be understood had aShape been a regular Shape object. (In fact, the aShape variable could have referred to an entirely different descendant of Shape, say Triangle, which also would not understand any Circle-specific methods calls or field accesses.) The compiler issues an error message if such an assignment is attempted. If you are absolutely sure that in this case the Shape variable is guaranteed to be pointing to a Circle object, you can use coercion to override the compiler: aCircle := Circle(aShape). Even then, at run time, if range checking is turned on, the assignment will be checked to make sure it is valid.

The point to remember from this is that even though a variable is declared to be of a particular object type, at run time, its actual type may be that type or any descendant of that type. It is by this means that one could have a list if "shapes" that could each be told to "draw", where the actual types are a mixed collection of circles, squares, triangles, and so forth. As a result, the determination of which actual method to call must be made at run time. This is accomplished using a "method dispatch routine" that looks at the two byte type field of the object and uses tables of method locations to direct the call to the proper method. Method dispatching will be discussed in more detail later.

A final syntactic addition to Pascal is the inherited keyword. If you have overridden a method to add some code specific to your object type but still want to use the code in the overridden method, you would use the word inherited followed by the method name:

 procedure MyController.ProcessKeystroke(ch: char);
 begin
 if ch = 'X' then
 DoSomethingSpecial
 else
 inherited ProcessKeystroke(ch);
 end;
 

Assuming Controller was the immediate ancestor of MyController, the inherited call would be a call to Controller.ProcessKeystroke (given that the method exists). In the case of inherited, the proper method to call can always be determined at compile time -- there is no need for a run time method dispatch. The call is always to the closest ancestor that implemented the method. Realize that this does not necessarily mean the immediate ancestor. If the immediate ancestor did not implement the method but an ancestor further up in the hierarchy did, the call would be to that method. By using the inherited keyword rather than explicitly naming an ancestor (superclass) object type, you are insulated from possible future changes that might insert or delete an implementation of the method in an ancestor or superclass. The compiler will issue an error message if inherited is used in a method that was not inherited from an ancestor object type.

Object Pascal also provides the built-in function Member. You can use Member to test if a particular object is in a certain class. For example you could say:

 if Member(aShape, Circle) then
 numCircles := numCircles+1;

 

Member returns true if the object's type is the same type or a descendant of the object type being tested. In the example above, numCircles would be bumped for ordinary circles and any specialized subclasses of Circle, but not for Squares, Triangles, or ordinary Shapes. The use of the Member function is somewhat contrary to the principles of object-oriented programming (you're not supposed to peek at your own type) so its use is generally discouraged except in unusual circumstances.

Since all object references are stored as handles to the object data on the heap, there are a couple of Pascal constructs that are unsafe to use on fields of an object. One is the use of a field as a VAR parameter in a procedure call. The Pascal compiler pushes the address of a VAR parameter on the stack. If the heap were to compact while processing the procedure, the address of the field of the object could become invalid. Another situation where the compiler computes an absolute address is if you use the with statement on an object field that is a record type, eg: "with aCircle.bounds do". If some statement in the with block caused a compaction, the computed address could become invalid. The compiler issues warnings when such a usage is detected. If you are sure the procedure or with statement will not compact the heap, you can precede the statement with the {$H-} compiler option. This tells the compiler not to issue the warning. You should follow the statement with {$H+} to turn heap warnings back on.

Levels of Compatibility

If you are a compiler writer who wants to use MacApp, there are a variety of levels of compatibility you can strive to achieve. The degree of compatibility can fall into the conceptual, the source file, or the object file level. (Note: The term object is used in two completely different contexts in this article. One use is with object-oriented phrases such as object type or an object on the heap. The second use is when speaking of the object file format, which is the term used for the structure of a file generated by a compiler.) To be conceptually compatible, the language must support the object-oriented concepts of object type definitions, inheritance, and method calls. Source file compatibility is a special case that only applies to Pascal compilers. The Pascal compiler would have to support all of the extensions to Pascal that the Macintosh Workshop Pascal supports, which in addition to the object-oriented extensions, includes such features as separately compiled units, expressions in constant declarations, and numerous compile time options such as conditional compilation. Finally, languages that are object file compatible would use the same object file format, namely that defined in the appendix of the MPW reference manual, and furthermore would support the specific method calling conventions and method table formats that Object Pascal generates.

If a language supports the object-oriented concepts of Object Pascal and if the programming structures of the language resemble the structures of Pascal, it should be fairly simple to write a program to do an automatic translation of MacApp into the desired language. For known constructs that are not automatically translatable, the program would flag that code for hand translation. The compiler writer could then distribute either the translated source or the compiled object form of MacApp, subject, of course, to whatever legal mumbo jumbo is required from Apple to redistribute MacApp. For compilers that do not use the MPW object file format, some variation of this will be the only alternative for those who want to use MacApp.

Pascal compilers that add the extensions of Macintosh Workshop Pascal can directly compile the MacApp sources themselves. If your compiler supports most but not all of the extensions, you may be able to modify the MacApp source files to not use the unsupported features. The object-oriented extensions would, of course, have to be supported.

Compilers that generate code using the MPW object file format and use the Object Pascal method table and method calling schemes will be able to link directly with compiled MacApp files. They will be able to link with Object Pascal building block files such as the Text and Dialog Box units.

Currently, most compilers do not support the MPW object file format. Hence, the only option available is that of translating MacApp into their particular language (which may be a trivial or null translation in the case of Object Pascal compatible compilers). If you as a compiler writer are not religiously (or pragmatically) devoted to your particular object file format, I would encourage you to consider using that of MPW. In any event, the discussion of our particular method table organization and method dispatching scheme that follows should be useful if you are considering adding object-oriented features to your language, even if you decide to implement your language in a completely different way.

Object File Format

As mentioned before, Apple's Object Pascal compiler generates files using the MPW object file format. The structure of the file consists of a collection of varying length records. There are eleven different kinds of records. The important ones for our purposes are the module, contents, and reference records. The module record specifies a new code or data module. Each procedure or function is represented by a code module. It is generally followed by one or more reference and/or contents records. The reference records specify what external modules are referenced from the current module. The contents records contain the actual code of the routine. The linker uses the reference and module records to patch branches and other instructions that have external references.

The Class Info Proc

When an object type is defined, a phony code module is generated. This code module is known as the "Class Info Proc". It contains information on who the ancestor is, what the size of an object of this type will be, and how many methods are implemented by this type. This is followed by the actual method table. This module is never actually called. It is placed in a special segment named %_MethTables with all other class info proc's. This segment also contains a very short routine called %_RTS1. The code for it is simply an RTS instruction. At application startup, %RTS1 is called, which loads the segment with all of the method tables.

The Method Call

Before I talk about the format of the method table itself, we need to understand how a method call works. Consider the following method call:

 aShape.Move(dist);

Method calls in Object Pascal naturally use Pascal calling conventions. First the parameters are pushed onto the stack (in the order they appear in the procedure declaration), then a JSR (Jump to Subroutine) call is made. Recall that in a method, there is always the implicit parameter SELF. This is pushed onto the stack after the actual parameters of the method. The object code for the method call above will look somewhat like this:

 MOVE.L dist,-(SP)
 MOVE.L aShape,-(SP)
 JSR    ??

Where does the JSR jump to? Since the actual method to call is dependent on the run time type of aShape, we cannot put a direct JSR to Shape.Move. At run time, aShape could be a Circle, Square, or some descendent not even known when this code was compiled. We need to go through a dispatch mechanism that examines the object to determine its type and then call the appropriate method based on that type. But so far, looking at the code above, we haven't even indicated what method we want to call. Most object-oriented languages use the selector technique to indicate to the dispatching routine what method is being called. A selector is some unique identifier for a particular method name. Often the selector is simply the name of the method itself. This, however can be expensive in terms of space required. Furthermore, Object Pascal allows methods in unrelated branches of the object hierarchy to have the same name with completely different parameter lists. The compiler treats these as totally separate method definitions. Simply using the method name for the selector would be ambiguous.

The Selector Proc

The question remains: how do we generate a unique selector for each method name? We let the linker do it! The linker, in resolving cross segment references, patches JSR's by having them branch into a jump table that then jumps to the correct routine. When segments are unloaded and reloaded in memory, the jump table entries are updated appropriately. The jump table is stored near an address pointed to by register A5. All JSR's into the jump table are of the form JSR x(A5) where x is some offset into the jump table. It is this offset x that the linker generates that we use as the method selector. As each new method name is encountered during the compilation of an object type definition, a very short procedure is generated. This procedure is referred to as the "selector proc". Its name is of the form TypeName$MethodName, such as Shape$Move. Note that the selector proc is not generated for method overrides, only when the method definition is first encountered. Methods by the same name in an unrelated branch of the hierarchy would have a selector proc, for example Employee$Move. The contents of the selector proc is simply a JSR to the actual method dispatching routine, called %_Method.

It is to the appropriate selector proc that all method calls are directed. The JSR instruction above would therefore be JSR Shape$Move. All selector procs are placed in another special segment, "%_SelProcs". All references to it are guaranteed to be through the jump table. The critical significance of this is that when the JSR is patched by the linker, the two-byte offset into the jump table is unique for that method name. The method dispatch routine examines those bytes and matches them against values stored in the method tables to determine what method is being called. Which brings us back to the format of the method tables.

Method Table Format

As mentioned above the method table for a particular object type appears at the end of the class info proc. The table is simply a list of pairs of references, one pair for each method implemented by this type. The first reference in each pair is to the selector proc and the second is to the actual method implementation. Each of these references are guaranteed to be across segments. Normally, when the linker is resolving a cross segment reference, it not only patches the offset bytes of the instruction, it also sets the bits in the instruction itself to make it A5 relative. For the method tables, there are no JSR's, just offsets that need to be patched. Fortunately there is a special bit (the A5-relative flag) in the reference record to tell the linker not to attempt to edit the word before the offset location.

Objects on the Heap and the New Routine

Objects are created using the New procedure. The compiler detects whether the parameter is an object type variable. It calls a quite different procedure than the normal New for pointer types. This procedure, %_OBNEW, allocates the data on the application heap. (Normal pointer New calls get data allocated on a special Pascal Heap.) %_OBNEW must also set the two byte class identifier field for the object. Like almost every other two byte field we've seen so far, this is an A5 offset into the jump table. This time the reference is to the class info proc of the class of the parameter to New. The actual calling sequence for New(aCircle) is:

 PEA    aCircle
 PEA    Circle's Class Info Proc + 2
 MOVE.W #size of instance,-(SP)
 JSR    %_OBNEW

The "+ 2" for the class info proc is somewhat of a hack. The jump table entry for the class info proc is JMP x where x is the address of the class info proc. We don't want to execute the code there, we just want to look at the information in it. By bumping the pointer by two we are in effect creating a handle to the class info proc, where the master pointer is the address stored after the JMP instruction in the jump table. %_OBNEW calls a routine %_SetClassIndex that subtracts A5 from this "handle" and stuffs the result into the two byte type identifier field. When a method is called, %_Method adds A5 back to the two byte field of the object, thus reconstructing the handle to the class info proc.

Figure 2.

The Method Dispatch Routine

In Figures 2 and 3 we see how a typical method call works. As we saw before, the parameters of the method, if any, are pushed onto the stack followed by the handle to the object itself. We then do a JSR to the selector proc, in this case Shape$Rotate. Shape$Rotate, like all selector procs, is simply a JSR to %_Method, the dispatch routine. In the method dispatch routine we first grab the handle to the object from the stack. We then extract the class identifier bytes from the object header. Adding A5 to these bytes gives us a handle to Circle's class info proc. The method "selector" is the two offset bytes after the JSR instruction to the selector proc. We search through the method table in the class info proc for a match to this selector. Since Rotate is not overridden in Circle, we do not find a match in this table. We then find the class info proc of the superclass, namely that of Shape. We go through the same search for the method selector and this time we do find a match for Rotate. We then jump to the proper routine, Shape.Rotate.

Figure 3.

Type Checking

Previously I mentioned that if you use type coercion to do an assignment of one object type variable to another, a run time check would be generated. The check is to see if the type of the object being assigned is the same type or a descendant type of the variable on the left side of the assignment. This is the same check that is made when you call the Member function. The routine that does this is called %_OBCHK. It takes two parameters, a handle to the object and a pointer to the jump table entry of the class info proc for the class whose membership is being tested. %_OBCHK returns the object handle if the test succeeds and nil if it fails. It calls the routine boolean routine %_InObj to do the actual test.

The information presented thus far should be sufficient for someone to implement object-oriented features in their language. The following discussion of "Object Assembler" is an example of how we took another "language" and generated the same method table formats and so forth to create an object file compatible alternative to programming exclusively in Object Pascal.

Object Assembler

Despite the many advantages of using a higher level language, we wanted to be able to escape to assembly language when necessary to efficiently code-time critical parts of an application. Using the powerful macro language available with the MPW 68000 Assembler, I was able to write a set of macros that allows one to define a class, implement and call methods, and create new objects, all in 68000 assembly language.

For example, the Shape and Circle definitions we saw in Pascal would look like:

 ObjectDefShape,,\
 (bounds,8),\
 (color,2), \
   METHODS, \
 (Draw),\
 (Erase), \
 (Rotate),\
 (Move),\
 (Area)

 ObjectDefCircle,Shape,   \
 (radius,2),\
   METHODS, \
 (Draw,OVERRIDE),\
 (Area,OVERRIDE),\
 (SetRadius)

(The '\' character is required by the Assembler when continuing a line)

The ObjectDef macro actually generates the class info proc and selector procs as specified earlier. It also sets up data structures for allowing field accesses and method calls later in the code. A method is defined as follows:

 Erase: ProcMethOf Shape
 LINK   #0,A6
 MoveSelf A0
 MOVE.L (A0),A0
 PEA    bounds(A0)
 _EraseRect
 UNLK   A6
 MOVE.L (SP),(SP)+
 RTS
 EndMethod

The ProcMethOf macro (and the FuncMethOf macro) invoke another macro, ObjectWith, that allows field references like bounds(A0) to work properly. MoveSelf is a simple macro that gets SELF off of the stack. It assumes that you started the method with a LINK A6. The routine above loads SELF into A0, dereferences it, and pushes the bounds field onto the stack so that EraseRect can be called. After the Unlink, the stack is fixed up by stuffing the return address on top of the single parameter, SELF, and the method returns.

Method calls are made using the MethCall macro:

 MoveSelf -(SP)
 MethCall Draw,Shape

MethCall generates a JSR to the proper Selector proc for Draw. If the call was made from inside of a method of Shape or a subclass of Shape, the parameter Shape could have been omitted. The other important macros are Inherited and NewObject:

 MoveSelf -(SP)
 InheritedDraw

 NewObject10(A6),Circle

Inherited behaves as in Object Pascal. NewObject requires a memory reference parameter and a type name. The handle of the new object is stored into the memory location specified by the parameter.

A full description of the macros available is contained in both the MPW Assembler Manual and the MacApp Reference Guide. Since these macros generate the same code that Object Pascal does, any code written in "Object Assembly" language can be linked with MacApp object files. In fact, specific methods in Object Pascal can be declared external and coded in assembly language using the macros. In addition, the assembled files can be run through the Optimizer described in the next section.

The Optimizer and the New Run Time Environment

In running sample applications written in MacApp, the performance has been quite good, despite the fact that every method call must go through the method dispatch mechanism before being executed. However, we realized that some significant optimizations were possible once the entire object type hierarchy was known. We have developed an optimizer program that processes the object files just before they are linked. It builds an internal representation of the entire object type hierarchy and proceeds to analyze it for potential optimizations.

Treatment of Monomorphic Methods

The most significant savings arises from being able to identify those methods that are implemented in only one object type, in other words, methods that are never overridden. Since these "monomorphic" methods have only one implementation, there is no need for a call to them to go through method dispatching. The Optimizer reroutes calls to these methods to jump directly to the method. Recall that originally the call was to a "selector proc" that in turn called the method dispatch routine. Not only does this increase the speed dramatically for these method calls but the space required is reduced. There no longer needs to be a selector proc nor the jump table entry that pointed to it. Also the entry in the method table for that method can be eliminated.

We have found that approximately 75% of the methods defined in MacApp applications are monomorphic. These include many internal methods of the MacApp classes themselves. For any "leaf" object type, one that has no descendants, any new method it defines will be monomorphic.

Transposition of the Method Tables for Polymorphic Methods

For the remaining "polymorphic" methods, those methods that are implemented by more than one class, the now reduced method tables are transposed. That is, where before each method table was a list of the methods implemented by a particular class, now each table is a list of classes that implement a particular method. This results in more tables of shorter length. Previously the method tables were stored in the class info procs. Now the tables are stored in the selector procs. In fact, they are stored immediately after the JSR instruction that jumps to the method dispatch routine. Thus the address of the method table is on the top of the stack when the dispatch routine is entered.

The form of this new method table is a list of two byte pairs. The first element in the pair is a class number that is generated by the optimizer. The second element is a reference to the actual implementation of the method, which the linker has patched with an A5 offset into the jump table. (This element is unchanged from the previous method table format.) The class number of a particular class is always greater than that of its superclass. The entries in the method table are sorted in descending order of class number. By numbering the classes in this way and keeping a separate table of superclasses, the method dispatch routine can properly search the method table. A Class number is always an even positive integer, making accesses to the superclass table simpler. A handle to the superclass table is stored in a low memory location.

The New Object Header

In the optimized run time environment objects have a different two byte class identifier than before. The A5 offset to the jump table entry of the class info proc is replaced by the class number that was generated by the optimizer. The class info proc no longer contains the method tables or a reference to the superclass. In fact it has just a single two byte entry, namely the class number. The calling sequence to %_OBNEW is the same as before, but the Optimizer actually redirects the %_SetClassIndex call in %_OBNEW to instead call %_OptSetCI. %_OptSetCI gets the class number from the class info proc and stuffs it into the object header.

The New Method Dispatch Routine

The new method table format requires a new method dispatching scheme. In fact, this is the scheme used by the method dispatch routine in the 128K ROM. The routine is also available in the libraries for 64K ROM machines.

A somewhat complicated algorithm is used to check only those methods that belong to the hierarchy of the object processing the method. For example, following the example in Figures 4 and 5, if aShape.Rotate was called and aShape was currently a Circle object the search would proceed as follows. First check to see if the most recent search was for a Circle by checking the cache at the beginning of the table. If we get a match, we jump immediately to the method (via the cached jump table offset). If we don't get a match, then check each entry in the table until we do get a match OR we arrive at a class number that is less than the current number. Since the current number is 8, we will skip the 10 (square.rotate) and stop at 6 (triangle.rotate). If we match, we jump to the method. It isn't a match, so now we look up Circle's superclass in the superclass table. The superclass is 4 (shape), and we proceed as before searching for a match or a number less than the current number (now 4). The next entry is a 4 so we match. Before jumping to the method, we stuff the original class number 8 in the class number cache and the jump table offset for shape.rotate in the method cache. If the next call to Rotate is for a circle object we'll get an immediate hit in the cache.

Figure 4.

The Optimizer also redirects the %_InObj routine to %_OptInObj. This is the boolean function that tests object membership in a particular class. The new routine uses the class number of the object and the superclass table to test the object.

Figure 5.

Future Optimizations

Other optimizations are possible when the entire class hierarchy is known. Say a variable aCircle is declared to be of type Circle and furthermore that there is no descendant of Circle defined. Then any method call that aCircle makes can be resolved before run time. This is because any object type variable can only reference an object whose type is the declared class or a descendant of that class. Since Circle has no descendants we know that aCircle must be a Circle object. Therefore we know that if Draw is invoked, we should call Circle.Draw and if Erase is invoked we call Shape.Erase (since Circle did not implement Erase) and so on. This is a little more difficult to implement and has not yet been put into the Optimizer.

Supporting the MacApp Debugger

One of the most appealing features of MacApp is its powerful debugger. The debugger is independent of the object-orientedness of MacApp. When an application is compiled with debugging flags turned on, the MacApp debugger is installed in a separate window on the screen. At any time while running the application you can go into the debugger and look at the stack or see a recent history of procedure calls. You can examine objects on the heap and even set up intentional error conditions such as nil object handles. You can set break points at specific methods and you can step through the code a method at a time. There are many other useful features.

To support the debugger, the Object Pascal compiler inserts a call to a special routine called BP at the beginning of every routine. It also inserts a call to EP at the end of every routine and a call to EX for Exits or GOTO's that jump out of a routine. In addition, the name of each routine is appended after the code of the routine. BP, EP, and EX are implemented in the UTrace unit that comes with MacApp. If you want to support the MacApp debugger, you should look at how it works and see if you can tailor it to your particular language.

The C+- Language

We at Apple are anxious to see more compilers support object-oriented concepts and be able to make use of the several man-years of effort that went into developing MacApp. In particular we recognize the popularity of the C language and the proliferation of C compilers available on the Macintosh. Since the C compiler that comes with MPW was done by a third party and they were under considerable time pressure to deliver as it was, we were not able to get object-oriented features put into it.

In the meantime, however, we have come up with a recommended specification for an object-oriented C. It is based on the C++ language from Bell Labs. It is essentially a subset of C++ that includes just those features necessary to support MacApp. We call this language C+-. The specification is available as a technical report from the Apple Library. Of course, if you want to support the full C++ specification, you will still be compatible with C+-.

How to Get Help

If you do decide to use some of the information presented in this article in order to be able to use MacApp, contact Harvey Alcabes at Apple. Harvey is the Product Manager for MacApp and is coordinating third party efforts to add objects to their languages. He can advise you on any licensing requirements for distributing translated versions of MacApp and so forth.

References

Object Pascal Report, by Larry Tesler; Feb 22, 1985, published in Structured Language World, Volume 9, No 3.

The MacApp Programmers Reference Guide

C+- Specification, by Larry Tesler; May 26, 1986

Apple Technical Report #2

Object-Oriented Programming for the Macintosh, by Kurt Schmucker; 1986; Published by Hayden Press

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links... | Read more »
Price of Glory unleashes its 1.4 Alpha u...
As much as we all probably dislike Maths as a subject, we do have to hand it to geometry for giving us the good old Hexgrid, home of some of the best strategy games. One such example, Price of Glory, has dropped its 1.4 Alpha update, stocked full... | Read more »
The SLC 2025 kicks off this month to cro...
Ever since the Solo Leveling: Arise Championship 2025 was announced, I have been looking forward to it. The promotional clip they released a month or two back showed crowds going absolutely nuts for the previous competitions, so imagine the... | Read more »
Dive into some early Magicpunk fun as Cr...
Excellent news for fans of steampunk and magic; the Precursor Test for Magicpunk MMORPG Crystal of Atlan opens today. This rather fancy way of saying beta test will remain open until March 5th and is available for PC - boo - and Android devices -... | Read more »
Prepare to get your mind melted as Evang...
If you are a fan of sci-fi shooters and incredibly weird, mind-bending anime series, then you are in for a treat, as Goddess of Victory: Nikke is gearing up for its second collaboration with Evangelion. We were also treated to an upcoming... | Read more »
Square Enix gives with one hand and slap...
We have something of a mixed bag coming over from Square Enix HQ today. Two of their mobile games are revelling in life with new events keeping them alive, whilst another has been thrown onto the ever-growing discard pile Square is building. I... | Read more »
Let the world burn as you have some fest...
It is time to leave the world burning once again as you take a much-needed break from that whole “hero” lark and enjoy some celebrations in Genshin Impact. Version 5.4, Moonlight Amidst Dreams, will see you in Inazuma to attend the Mikawa Flower... | Read more »
Full Moon Over the Abyssal Sea lands on...
Aether Gazer has announced its latest major update, and it is one of the loveliest event names I have ever heard. Full Moon Over the Abyssal Sea is an amazing name, and it comes loaded with two side stories, a new S-grade Modifier, and some fancy... | Read more »
Open your own eatery for all the forest...
Very important question; when you read the title Zoo Restaurant, do you also immediately think of running a restaurant in which you cook Zoo animals as the course? I will just assume yes. Anyway, come June 23rd we will all be able to start up our... | Read more »
Crystal of Atlan opens registration for...
Nuverse was prominently featured in the last month for all the wrong reasons with the USA TikTok debacle, but now it is putting all that behind it and preparing for the Crystal of Atlan beta test. Taking place between February 18th and March 5th,... | Read more »

Price Scanner via MacPrices.net

AT&T is offering a 65% discount on the ne...
AT&T is offering the new iPhone 16e for up to 65% off their monthly finance fee with 36-months of service. No trade-in is required. Discount is applied via monthly bill credits over the 36 month... Read more
Use this code to get a free iPhone 13 at Visi...
For a limited time, use code SWEETDEAL to get a free 128GB iPhone 13 Visible, Verizon’s low-cost wireless cell service, Visible. Deal is valid when you purchase the Visible+ annual plan. Free... Read more
M4 Mac minis on sale for $50-$80 off MSRP at...
B&H Photo has M4 Mac minis in stock and on sale right now for $50 to $80 off Apple’s MSRP, each including free 1-2 day shipping to most US addresses: – M4 Mac mini (16GB/256GB): $549, $50 off... Read more
Buy an iPhone 16 at Boost Mobile and get one...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering one year of free Unlimited service with the purchase of any iPhone 16. Purchase the iPhone at standard MSRP, and then choose... Read more
Get an iPhone 15 for only $299 at Boost Mobil...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering the 128GB iPhone 15 for $299.99 including service with their Unlimited Premium plan (50GB of premium data, $60/month), or $20... Read more
Unreal Mobile is offering $100 off any new iP...
Unreal Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering a $100 discount on any new iPhone with service. This includes new iPhone 16 models as well as iPhone 15, 14, 13, and SE... Read more
Apple drops prices on clearance iPhone 14 mod...
With today’s introduction of the new iPhone 16e, Apple has discontinued the iPhone 14, 14 Pro, and SE. In response, Apple has dropped prices on unlocked, Certified Refurbished, iPhone 14 models to a... Read more
B&H has 16-inch M4 Max MacBook Pros on sa...
B&H Photo is offering a $360-$410 discount on new 16-inch MacBook Pros with M4 Max CPUs right now. B&H offers free 1-2 day shipping to most US addresses: – 16″ M4 Max MacBook Pro (36GB/1TB/... Read more
Amazon is offering a $100 discount on the M4...
Amazon has the M4 Pro Mac mini discounted $100 off MSRP right now. Shipping is free. Their price is the lowest currently available for this popular mini: – Mac mini M4 Pro (24GB/512GB): $1299, $100... Read more
B&H continues to offer $150-$220 discount...
B&H Photo has 14-inch M4 MacBook Pros on sale for $150-$220 off MSRP. B&H offers free 1-2 day shipping to most US addresses: – 14″ M4 MacBook Pro (16GB/512GB): $1449, $150 off MSRP – 14″ M4... Read more

Jobs Board

All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.