Open Bug 516046 Opened 15 years ago Updated 2 years ago

Gratuitous use of ObjC exceptions

Categories

(Firefox :: General, defect)

x86
macOS
defect

Tracking

()

People

(Reporter: joelr, Unassigned)

References

(Blocks 1 open bug)

Details

(Whiteboard: [ts])

Consider CFStringRefToUTF8 [1]. This function uses Carbon APIs but no Cocoa. Still the whole function is bracketed in NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT and NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT, i.e. 

@try {
  ... 
} @catch(NSException *_exn) {
  nsObjCExceptionLog(_exn);
}                                \
return NS_ERROR_FAILURE;

It looks harmless but take a look at the assembler dump below. 2/3 or 3/4 of this function (more?) are taken by the exception setup and cleanup. ObjC exceptions are zero-cost on x86-64 but very expensive on x86-32 [2]. I think we should analyze our code for gratuitous use of ObjC exceptions and, perhaps, eliminate their use altogether where possible. 

For example, the .mm extension is for C++ files that call into ObjC. I skimmed through half of nsLocalFileOSX.mm but I didn't see any calls into ObjC, just calls to Carbon which is a C/C++ API. Why are we using ObjC exception here?

[1] http://mxr.mozilla.org/mozilla-central/source/xpcom/io/nsLocalFileOSX.mm#2225 
[2] http://developer.apple.com/mac/library/releasenotes/Cocoa/RN-ObjectiveC/index.html

Breakpoint 1, CFStringReftoUTF8 (aInStrRef=0x2614fc0, aOutStr=@0xbfffec58) at ../../../mozilla-central/xpcom/io/nsLocalFileOSX.mm:2187
2187	  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
(gdb) disas
Dump of assembler code for function CFStringRefToUTF8:
0x00bf8ff0 <CFStringRefToUTF8+0>:	push   %ebp
0x00bf8ff1 <CFStringRefToUTF8+1>:	mov    %esp,%ebp
0x00bf8ff3 <CFStringRefToUTF8+3>:	push   %esi
0x00bf8ff4 <CFStringRefToUTF8+4>:	push   %ebx
0x00bf8ff5 <CFStringRefToUTF8+5>:	sub    $0xb0,%esp
0x00bf8ffb <CFStringRefToUTF8+11>:	call   0xbf9000 <CFStringRefToUTF8+16>
0x00bf9000 <CFStringRefToUTF8+16>:	pop    %ebx
0x00bf9001 <CFStringRefToUTF8+17>:	mov    %eax,-0x6c(%ebp)
0x00bf9004 <CFStringRefToUTF8+20>:	mov    %edx,-0x70(%ebp)
0x00bf9007 <CFStringRefToUTF8+23>:	lea    -0x64(%ebp),%esia
0x00bf900a <CFStringRefToUTF8+26>:	mov    %esi,(%esp)
0x00bf900d <CFStringRefToUTF8+29>:	call   0xd1b0f4 <dyld_stub_objc_exception_try_enter>
0x00bf9012 <CFStringRefToUTF8+34>:	mov    %esi,(%esp)
0x00bf9015 <CFStringRefToUTF8+37>:	call   0xd1aecc <dyld_stub__setjmp>
0x00bf901a <CFStringRefToUTF8+42>:	test   %eax,%eax
0x00bf901c <CFStringRefToUTF8+44>:	je     0xbf9080 <CFStringRefToUTF8+144>
0x00bf901e <CFStringRefToUTF8+46>:	lea    -0x64(%ebp),%esi
0x00bf9021 <CFStringRefToUTF8+49>:	mov    %esi,(%esp)
0x00bf9024 <CFStringRefToUTF8+52>:	call   0xd1b0e2 <dyld_stub_objc_exception_extract>
0x00bf9029 <CFStringRefToUTF8+57>:	mov    %eax,-0x74(%ebp)
0x00bf902c <CFStringRefToUTF8+60>:	mov    %esi,(%esp)
0x00bf902f <CFStringRefToUTF8+63>:	call   0xd1b0f4 <dyld_stub_objc_exception_try_enter>
0x00bf9034 <CFStringRefToUTF8+68>:	mov    %esi,(%esp)
0x00bf9037 <CFStringRefToUTF8+71>:	call   0xd1aecc <dyld_stub__setjmp>
0x00bf903c <CFStringRefToUTF8+76>:	test   %eax,%eax
0x00bf903e <CFStringRefToUTF8+78>:	je     0xbf9110 <CFStringRefToUTF8+288>
0x00bf9044 <CFStringRefToUTF8+84>:	lea    -0x64(%ebp),%eax
0x00bf9047 <CFStringRefToUTF8+87>:	mov    %eax,(%esp)
0x00bf904a <CFStringRefToUTF8+90>:	call   0xd1b0e2 <dyld_stub_objc_exception_extract>
0x00bf904f <CFStringRefToUTF8+95>:	mov    %eax,-0x74(%ebp)
0x00bf9052 <CFStringRefToUTF8+98>:	mov    -0x74(%ebp),%edx
0x00bf9055 <CFStringRefToUTF8+101>:	test   %edx,%edx
0x00bf9057 <CFStringRefToUTF8+103>:	jne    0xbf920a <CFStringRefToUTF8+538>
0x00bf905d <CFStringRefToUTF8+109>:	lea    -0x64(%ebp),%eax
0x00bf9060 <CFStringRefToUTF8+112>:	mov    %eax,(%esp)
0x00bf9063 <CFStringRefToUTF8+115>:	call   0xd1b0fa <dyld_stub_objc_exception_try_exit>
0x00bf9068 <CFStringRefToUTF8+120>:	mov    $0x80004005,%ebx
0x00bf906d <CFStringRefToUTF8+125>:	mov    %ebx,%eax
0x00bf906f <CFStringRefToUTF8+127>:	add    $0xb0,%esp
0x00bf9075 <CFStringRefToUTF8+133>:	pop    %ebx
0x00bf9076 <CFStringRefToUTF8+134>:	pop    %esi
0x00bf9077 <CFStringRefToUTF8+135>:	leave  
0x00bf9078 <CFStringRefToUTF8+136>:	ret    
0x00bf9079 <CFStringRefToUTF8+137>:	nopl   0x0(%eax)
0x00bf9080 <CFStringRefToUTF8+144>:	mov    -0x6c(%ebp),%eax
0x00bf9083 <CFStringRefToUTF8+147>:	mov    %eax,(%esp)
0x00bf9086 <CFStringRefToUTF8+150>:	call   0xd19336 <dyld_stub_CFStringGetLength>
0x00bf908b <CFStringRefToUTF8+155>:	mov    %eax,%esi
0x00bf908d <CFStringRefToUTF8+157>:	mov    %eax,-0x84(%ebp)
0x00bf9093 <CFStringRefToUTF8+163>:	movl   $0x0,-0x88(%ebp)
0x00bf909d <CFStringRefToUTF8+173>:	mov    -0x6c(%ebp),%ecx
0x00bf90a0 <CFStringRefToUTF8+176>:	lea    -0xc(%ebp),%eax
0x00bf90a3 <CFStringRefToUTF8+179>:	mov    %eax,0x20(%esp)
0x00bf90a7 <CFStringRefToUTF8+183>:	movl   $0x0,0x1c(%esp)
0x00bf90af <CFStringRefToUTF8+191>:	movl   $0x0,0x18(%esp)
0x00bf90b7 <CFStringRefToUTF8+199>:	movl   $0x0,0x14(%esp)
0x00bf90bf <CFStringRefToUTF8+207>:	movl   $0x0,0x10(%esp)
0x00bf90c7 <CFStringRefToUTF8+215>:	movl   $0x8000100,0xc(%esp)
0x00bf90cf <CFStringRefToUTF8+223>:	mov    -0x88(%ebp),%eax
0x00bf90d5 <CFStringRefToUTF8+229>:	mov    -0x84(%ebp),%edx
0x00bf90db <CFStringRefToUTF8+235>:	mov    %eax,0x4(%esp)
0x00bf90df <CFStringRefToUTF8+239>:	mov    %edx,0x8(%esp)
0x00bf90e3 <CFStringRefToUTF8+243>:	mov    %ecx,(%esp)
0x00bf90e6 <CFStringRefToUTF8+246>:	call   0xd19318 <dyld_stub_CFStringGetBytes>
0x00bf90eb <CFStringRefToUTF8+251>:	mov    $0x80004005,%ebx
0x00bf90f0 <CFStringRefToUTF8+256>:	cmp    %eax,%esi
0x00bf90f2 <CFStringRefToUTF8+258>:	je     0xbf9180 <CFStringRefToUTF8+400>
0x00bf90f8 <CFStringRefToUTF8+264>:	lea    -0x64(%ebp),%eax
0x00bf90fb <CFStringRefToUTF8+267>:	mov    %eax,(%esp)
0x00bf90fe <CFStringRefToUTF8+270>:	call   0xd1b0fa <dyld_stub_objc_exception_try_exit>
0x00bf9103 <CFStringRefToUTF8+275>:	mov    %ebx,%eax
0x00bf9105 <CFStringRefToUTF8+277>:	add    $0xb0,%esp
0x00bf910b <CFStringRefToUTF8+283>:	pop    %ebx
0x00bf910c <CFStringRefToUTF8+284>:	pop    %esi
0x00bf910d <CFStringRefToUTF8+285>:	leave  
0x00bf910e <CFStringRefToUTF8+286>:	ret    
0x00bf910f <CFStringRefToUTF8+287>:	nop    
0x00bf9110 <CFStringRefToUTF8+288>:	mov    -0x74(%ebp),%eax
0x00bf9113 <CFStringRefToUTF8+291>:	mov    %eax,0x4(%esp)
0x00bf9117 <CFStringRefToUTF8+295>:	mov    0x38b944(%ebx),%eax
0x00bf911d <CFStringRefToUTF8+301>:	mov    %eax,(%esp)
0x00bf9120 <CFStringRefToUTF8+304>:	call   0xd1b0e8 <dyld_stub_objc_exception_match>
0x00bf9125 <CFStringRefToUTF8+309>:	test   %eax,%eax
0x00bf9127 <CFStringRefToUTF8+311>:	je     0xbf9170 <CFStringRefToUTF8+384>
0x00bf9129 <nsObjCExceptionLog+0>:	mov    0x38b008(%ebx),%eax
0x00bf912f <nsObjCExceptionLog+6>:	mov    %eax,0x4(%esp)
0x00bf9133 <nsObjCExceptionLog+10>:	mov    -0x74(%ebp),%edx
0x00bf9136 <nsObjCExceptionLog+13>:	mov    %edx,(%esp)
0x00bf9139 <nsObjCExceptionLog+16>:	call   0xd1b100 <dyld_stub_objc_msgSend>
0x00bf913e <nsObjCExceptionLog+21>:	mov    %eax,%esi
0x00bf9140 <nsObjCExceptionLog+23>:	mov    0x38b00c(%ebx),%eax
0x00bf9146 <nsObjCExceptionLog+29>:	mov    %eax,0x4(%esp)
0x00bf914a <nsObjCExceptionLog+33>:	mov    -0x74(%ebp),%ecx
0x00bf914d <nsObjCExceptionLog+36>:	mov    %ecx,(%esp)
0x00bf9150 <nsObjCExceptionLog+39>:	call   0xd1b100 <dyld_stub_objc_msgSend>
0x00bf9155 <nsObjCExceptionLog+44>:	mov    %esi,0x8(%esp)
0x00bf9159 <nsObjCExceptionLog+48>:	mov    %eax,0x4(%esp)
0x00bf915d <nsObjCExceptionLog+52>:	lea    0x36ad48(%ebx),%eax
0x00bf9163 <nsObjCExceptionLog+58>:	mov    %eax,(%esp)
0x00bf9166 <nsObjCExceptionLog+61>:	call   0xd1a0ce <dyld_stub_NSLog>
0x00bf916b <nsObjCExceptionLog+66>:	jmp    0xbf905d <CFStringRefToUTF8+109>
0x00bf9170 <CFStringRefToUTF8+384>:	lea    -0x64(%ebp),%eax
0x00bf9173 <CFStringRefToUTF8+387>:	mov    %eax,(%esp)
0x00bf9176 <CFStringRefToUTF8+390>:	call   0xd1b0fa <dyld_stub_objc_exception_try_exit>
0x00bf917b <CFStringRefToUTF8+395>:	jmp    0xbf9052 <CFStringRefToUTF8+98>
0x00bf9180 <CFStringRefToUTF8+400>:	mov    -0x70(%ebp),%eax
0x00bf9183 <CFStringRefToUTF8+403>:	mov    -0xc(%ebp),%edx
0x00bf9186 <CFStringRefToUTF8+406>:	call   0xc34010 <_ZN19nsACString_internal9SetLengthEj>
0x00bf918b <CFStringRefToUTF8+411>:	mov    -0x70(%ebp),%eax
0x00bf918e <CFStringRefToUTF8+414>:	mov    0x4(%eax),%eax
0x00bf9191 <CFStringRefToUTF8+417>:	mov    $0x8007000e,%ebx
0x00bf9196 <CFStringRefToUTF8+422>:	cmp    -0xc(%ebp),%eax
0x00bf9199 <CFStringRefToUTF8+425>:	jne    0xbf90f8 <CFStringRefToUTF8+264>
0x00bf919f <CFStringRefToUTF8+431>:	mov    -0x70(%ebp),%ebx
0x00bf91a2 <_ZN19nsACString_internal12BeginWritingEv+0>:	mov    $0xffffffff,%edx
0x00bf91a7 <_ZN19nsACString_internal12BeginWritingEv+5>:	mov    %ebx,%eax
0x00bf91a9 <_ZN19nsACString_internal12BeginWritingEv+7>:	call   0xc35490 <_ZN19nsACString_internal13EnsureMutableEj>
0x00bf91ae <_ZN19nsACString_internal12BeginWritingEv+12>:	xor    %ecx,%ecx
0x00bf91b0 <_ZN19nsACString_internal12BeginWritingEv+14>:	test   %eax,%eax
0x00bf91b2 <_ZN19nsACString_internal12BeginWritingEv+16>:	je     0xbf91b6 <CFStringRefToUTF8+454>
0x00bf91b4 <_ZN19nsACString_internal12BeginWritingEv+18>:	mov    (%ebx),%ecx
0x00bf91b6 <CFStringRefToUTF8+454>:	mov    %esi,-0x7c(%ebp)
0x00bf91b9 <CFStringRefToUTF8+457>:	movl   $0x0,-0x80(%ebp)
0x00bf91c0 <CFStringRefToUTF8+464>:	mov    -0x6c(%ebp),%eax
0x00bf91c3 <CFStringRefToUTF8+467>:	lea    -0xc(%ebp),%edx
0x00bf91c6 <CFStringRefToUTF8+470>:	mov    %edx,0x20(%esp)
0x00bf91ca <CFStringRefToUTF8+474>:	mov    -0xc(%ebp),%edx
0x00bf91cd <CFStringRefToUTF8+477>:	mov    %edx,0x1c(%esp)
0x00bf91d1 <CFStringRefToUTF8+481>:	mov    %ecx,0x18(%esp)
0x00bf91d5 <CFStringRefToUTF8+485>:	movl   $0x0,0x14(%esp)
0x00bf91dd <CFStringRefToUTF8+493>:	movl   $0x0,0x10(%esp)
0x00bf91e5 <CFStringRefToUTF8+501>:	movl   $0x8000100,0xc(%esp)
0x00bf91ed <CFStringRefToUTF8+509>:	mov    -0x80(%ebp),%edx
0x00bf91f0 <CFStringRefToUTF8+512>:	mov    -0x7c(%ebp),%ecx
0x00bf91f3 <CFStringRefToUTF8+515>:	mov    %edx,0x4(%esp)
0x00bf91f7 <CFStringRefToUTF8+519>:	mov    %ecx,0x8(%esp)
0x00bf91fb <CFStringRefToUTF8+523>:	mov    %eax,(%esp)
0x00bf91fe <CFStringRefToUTF8+526>:	call   0xd19318 <dyld_stub_CFStringGetBytes>
0x00bf9203 <CFStringRefToUTF8+531>:	xor    %ebx,%ebx
0x00bf9205 <CFStringRefToUTF8+533>:	jmp    0xbf90f8 <CFStringRefToUTF8+264>
0x00bf920a <CFStringRefToUTF8+538>:	mov    -0x74(%ebp),%ecx
0x00bf920d <CFStringRefToUTF8+541>:	mov    %ecx,(%esp)
0x00bf9210 <CFStringRefToUTF8+544>:	call   0xd1b0ee <dyld_stub_objc_exception_throw>
End of assembler dump.
Whiteboard: [ts]
See bug 417560. As for why those handlers are covering Carbon calls, our understanding is that Carbon calls can raise Obj-C exceptions.
Josh,

1) How can Carbon calls raise ObjC exceptions, these are two completely different worlds. Carbon has absolutely nothing to do with the ObjC runtime (and thus exceptions) as far as I know.

2) I looked at bugs 417560 and 163260 but I maintain that we should not be this ham-handed. CFStringRefToUTF8 and other functions from nsLocalFileOSX are all over the place in the startup path. I want to streamline this path and I really don't want to see a disassembly like the above!

3) Carbon is 32-bit only and there's no 64-bit Carbon at all. None, zilch, none whatsoever. Will you be replacing these Carbon calls as part of the 64-bit port?
(In reply to comment #2)
> 1) How can Carbon calls raise ObjC exceptions, these are two completely
> different worlds. Carbon has absolutely nothing to do with the ObjC runtime
> (and thus exceptions) as far as I know.

At an implementation level they are not different worlds. Many Obj-C objects are backed by Carbon implementations, and in some cases the situation is reversed. According to the research we did, Carbon APIs can call into Obj-C code and frameworks and thus raise Obj-C exceptions.

> 2) I looked at bugs 417560 and 163260 but I maintain that we should not be this
> ham-handed. CFStringRefToUTF8 and other functions from nsLocalFileOSX are all
> over the place in the startup path. I want to streamline this path and I really
> don't want to see a disassembly like the above!

See my answer to question #1 :) I don't like it either, but we have not come up with a better plan.

> 3) Carbon is 32-bit only and there's no 64-bit Carbon at all. None, zilch, none
> whatsoever. Will you be replacing these Carbon calls as part of the 64-bit
> port?

This isn't really true, "Carbon" is a somewhat wishy washy term these days. Most of Carbon disappeared, not all of it. For example, there is FSRef functionality in 64-bit, though some FSRef-based APIs are not available. In any case, most of our Carbon usage will go away in 64-bit. We've removed huge amounts of it already.
FSRef lives in the CoreServices framework, particularly in CarbonCore.

nm
/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/CarbonCore
| grep objc
         U _objc_collectingEnabled
         U _objc_registerThreadWithCollector
         U _objc_unregisterThreadWithCollector

I do not see how this can throw ObjC exceptions.

I'm still waiting for the "official" word.
I meant that I'm waiting for the semi-official word from Apple folks on whether Carbon can raise ObjC exceptions and how to best handle them on x86-32.
Via Eric Schlegel at Apple:

Generally, exceptions should be caught at the level of the callback, rather than around the lower-level functions that you're calling in CoreFoundation.

As to whether the CF function you're calling could actually throw an exception, I can't say for sure, although it would be surprising. I don't think CoreFoundation throws exceptions in general. However, the source is public; you could download it and determine the answer yourself.

-eric
The CoreFoundation sources are available for perusal here:

http://www.opensource.apple.com/source/CF/CF-476.19/

We can easily check to see what throws exceptions and what doesn't. The Core Foundation bits we use (CFRunLoop, etc.) do not throw ObjC exceptions.
Blocks: 447581
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.