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

Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
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 below... | Read more »

Price Scanner via MacPrices.net

Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more
Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more

Jobs Board

*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
IT Systems Engineer ( *Apple* Platforms) - S...
IT Systems Engineer ( Apple Platforms) at SpaceX Hawthorne, CA SpaceX was founded under the belief that a future where humanity is out exploring the stars is Read more
Nurse Anesthetist - *Apple* Hill Surgery Ce...
Nurse Anesthetist - Apple Hill Surgery Center Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
Housekeeper, *Apple* Valley Village - Cassi...
Apple Valley Village Health Care Center, a senior care campus, is hiring a Part-Time Housekeeper to join our team! We will train you for this position! In this role, Read more
Sublease Associate Optometrist- *Apple* Val...
Sublease Associate Optometrist- Apple Valley, CA- Target Optical Date: Apr 20, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92307 **Requisition Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.