[Openmcl-devel] PDFKit crashes

Gary Byers gb at clozure.com
Wed May 27 20:13:24 PDT 2009


On Wed, 27 May 2009, Ron Garret wrote:

> On May 27, 2009, at 4:19 PM, Ron Garret wrote:
>> Trying to open and display a PDF file crashes CCL in various ways.
>> Using this code:
>> (in-package "CL-USER")
>> (require :cocoa)
>> (objc:load-framework "Quartz" :quartz)
>> (defun nsstr (s) (make-instance 'gui::ns-lisp-string :string s))
>> (defun pdf-from-file (filename)
>>  (#/initWithData: (#/alloc ns:pdf-document)
>>                   (#/dataWithContentsOfFile: ns:ns-data filename)))
>> (defun pdf-from-url (url)
>>  (#/initWithUrl: (#/alloc ns:pdf-document)
>>                  (#/URLWithString: ns:ns-url url)))
> So it turns out that this problem is due to misspelling initWithURL as
> initWithUrl.  Still, it would be good if the result of this were not a
> hard crash.

An ObjC message named #/description is applicable to all NSObjects; it
returns an NSString which ... um, describes the object:

? (#/description (#/init (#/alloc ns:ns-object)))
#<NS-MUTABLE-STRING "<NSObject: 0x12994200>" (#x12943380)>

There isn't too much to say about a direct instance of NS:NS-OBJECT, but
for instances of other classes there's often quite a bit of information
in the #/description string.  The default PRINT-OBJECT method for NS:NS-OBJECTs
invokes #/description, obtains a lisp string from the ns-string, and uses
the resulting string (all of it ...) in its output.  (As I was typing
this, I saw a message suggesting that this output observe *PRINT-LENGTH*;
that isn't relevant to the printing of strings, but truncating the string to 
some reasonable length would probably be reasonable.)

In most cases, the existing #/description methods are remarkably robust,
but in at least some cases - most of which seem to involve objects that
have been allocated but not yet initialized - they leap before looking
and try to print uninitialized slots in the object:

? (#/description (#/alloc ns::pdf-document))
Unhandled exception 10 at 0x1451647a, context->regs at #xb029b970
Exception occurred while executing foreign code
  at .objc_category_name_NSColor_PDFColorUtilities + 99450

which looks kind of familiar.  So yes, you should have gotten a runtime
when trying to call a misspelled ObjC method name (which was warned about)
and in fact you did.  The system started to enter a break loop (the good
news: on the event thread in the context in which the error occurred; the
bad news: using AltConsole, until something better comes along) but crashed
trying to print the (uninitialized) NS:NS-PDF-DOCUMENT object by printing
the string returned by its #/description method.  (Look at the backtrace.)

I don't know how to tell in general whether or not it's safe to call a
#/description method on an arbitrary NSObject (it certainly seems to
be in the vast majority of cases, but it may be hard to recover from
the cases where it isn't.)  Until this point, I'd been fairly
impressed at how robust #/description methods are, but that enthusiasm
is certainly tempered by cases like this.  At the very least, in cases
where the output is unsolicited (error messages), it'd likely be wiser
to not call #/description when printing an NSObject unless we're more
sure than we can be in general that that won't sail off into the ozone.

> Still puzzling over the second problem.

Both your earlier email message and the bug report seem to contain
references to something named "sce", which doesn't seem to be defined

In the code that I can see, you seem to be calling #/setDocument: on
an NS:PDF-VIEW that's been allocated but not initialized.


? (allocate-instance 'some-standard-clos-class)

returns (in the general case) an instance of the specified class where
all of the instance's slots are unbound; that's likely not to be as
useful an object as the result of MAKE-INSTANCE would have been.

Similarly, the NSObject returned by a call to #/alloc on its class
isn't yet initialized and is unlikely to behave itself; you generally
don't want to do anything with the result of a call to #/alloc but
immediately call some flavor of #/init... on it.)  ObjC's #/alloc
and #/init are similar to ALLOCATE-INSTANCE and INITIALIZE-INSTANCE,
but differ in some subtle ways (an #/init method may deallocate its
argument and return some other instance.)  It's often saner to
avoid this gunk and simply use MAKE-INSTANCE:

(make-instance some-objc-class)

is equivalent to

(#/init (#/alloc some-objc-class)) ; all NSObjects respond to #/init


(make-instance some-objc-class :with-x x :and-y y)

is equivalent to

(#/initWithX:andY: (#/alloc some-objc-class) x y)

but may be less error-prone.

> rg
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list