ART世界探险(10) - 异常处理

ART世界探险(10) - 异常处理

对于编译Java的话,有一个问题不能不考虑,就是异常处理的问题。异常处理是基于Java的语句块的,翻译成本地代码的话,需要针对这些指令的地址进行一下重排。
我们来看下ART是如何实现异常处理的。

Java异常处理

首先复习一下Java。
Java有两种Exception,一种是普通Exception,另一种是RuntimeException。非RuntimeException,如果没有处理,就是没有用try...catch块包围或者是throws声明的话,会编译不过。
而RuntimeException就没有这个限制,不处理,就等运行时crash再说吧。

Java源代码

我们先写两个小函数试验一下:

    public void withException(){
        throw new IllegalStateException();
    }

    public void dealWtihException() throws IOException{
        try{
            withException();
        }catch(Exception e){
            if(e instanceof RuntimeException) throw new IOException();
        }
    }

Class字节码

从字节码上我们可以看到,dealWithException中,0到4行是记录在Exception table中的,如果出了Exception,则会跳转到7开始的语句。
而withException方法,因为抛出的是RuntimeException,也没有try...catch,所以没有Exception table.

  public void withException();
    Code:
       0: new           #11                 // class java/lang/IllegalStateException
       3: dup
       4: invokespecial #12                 // Method java/lang/IllegalStateException."<init>":()V
       7: athrow

  public void dealWtihException() throws java.io.IOException;
    Code:
       0: aload_0
       1: invokevirtual #13                 // Method withException:()V
       4: goto          23
       7: astore_1
       8: aload_1
       9: instanceof    #15                 // class java/lang/RuntimeException
      12: ifeq          23
      15: new           #16                 // class java/io/IOException
      18: dup
      19: invokespecial #17                 // Method java/io/IOException."<init>":()V
      22: athrow
      23: return
    Exception table:
       from    to  target type
           0     4     7   Class java/lang/Exception

Dalvik指令和OAT指令

我们先看只抛个Exception的withException

  7: void com.yunos.xulun.testcppjni2.SampleClass.withException() (dex_method_idx=16784)
    DEX CODE:
      0x0000: 2200 3908                 | new-instance v0, java.lang.IllegalStateException // type@2105
      0x0002: 7010 0642 0000            | invoke-direct {v0}, void java.lang.IllegalStateException.<init>() // method@16902
      0x0005: 2700                      | throw v0
    OatMethodOffsets (offset=0x002778f4)
      code_offset: 0x00663bdc 
      gc_map: (offset=0x002848e8)
    OatQuickMethodHeader (offset=0x00663bc0)
      mapping_table: (offset=0x002deece)
      vmap_table: (offset=0x0030e0fa)
      v65535/r30
    QuickMethodFrameInfo
      frame_size_in_bytes: 32
      core_spill_mask: 0x40000000 (r30)
      fp_spill_mask: 0x00000000 
      vr_stack_locations:
        locals: v0[sp + #16]
        ins: v1[sp + #40]
        method*: v2[sp + #0]
        outs: v0[sp + #8]
    CODE: (code_offset=0x00663bdc size_offset=0x00663bd8 size=116)...
      0x00663bdc: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00663be0: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      GC map objects:  v1 ([sp + #40])
      0x00663be4: f81e0fe0  str x0, [sp, #-32]!
      0x00663be8: f9000ffe  str lr, [sp, #24]
      0x00663bec: b9002be1  str w1, [sp, #40]
      0x00663bf0: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00663bf4: 35000290  cbnz w16, #+0x50 (addr 0x663c44)

前面还是保存现场,检查有没有被suspend.
然后调用pAllocObject去new一个IllegalStateException对象。
v0是sp+16

      0x00663bf8: f94003e1  ldr x1, [sp]
      0x00663bfc: 52810720  mov w0, #0x839
      0x00663c00: f940d65e  ldr lr, [tr, #424] (pAllocObject)
      0x00663c04: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      GC map objects:  v1 ([sp + #40])
      0x00663c08: b90013e0  str w0, [sp, #16]
      0x00663c0c: b94013e0  ldr w0, [sp, #16]
      0x00663c10: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0002
      GC map objects:  v0 ([sp + #16]), v1 ([sp + #40])

只要是对象,就要调用构造方法,下面就是计算和调用构造方法。

      0x00663c14: b9000fe0  str w0, [sp, #12]
      0x00663c18: b9400fe1  ldr w1, [sp, #12]
      0x00663c1c: f94003e0  ldr x0, [sp]
      0x00663c20: b9400400  ldr w0, [x0, #4]
      0x00663c24: d2820810  mov x16, #0x1040
      0x00663c28: f2a00050  movk x16, #0x2, lsl #16
      0x00663c2c: f8706800  ldr x0, [x0, x16]
      0x00663c30: f940181e  ldr lr, [x0, #48]
      0x00663c34: d63f03c0  blr lr
      suspend point dex PC: 0x0002
      GC map objects:  v0 ([sp + #16]), v1 ([sp + #40])

从v0(sp+16)把异常对象的引用读回来,然后调用pDeliverException将其抛出去。

      0x00663c38: b94013e0  ldr w0, [sp, #16]
      0x00663c3c: f942225e  ldr lr, [tr, #1088] (pDeliverException)
      0x00663c40: d63f03c0  blr lr
      suspend point dex PC: 0x0005
      GC map objects:  v0 ([sp + #16]), v1 ([sp + #40])
      0x00663c44: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x00663c48: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      GC map objects:  v1 ([sp + #40])
      0x00663c4c: 17ffffeb  b #-0x54 (addr 0x663bf8)

下面再看dealWtihException的:

  1: void com.yunos.xulun.testcppjni2.SampleClass.dealWtihException() (dex_method_idx=16778)
    DEX CODE:
      0x0000: 6e10 9041 0200            | invoke-virtual {v2}, void com.yunos.xulun.testcppjni2.SampleClass.withException() // method@16784
      0x0003: 0e00                      | return-void
      0x0004: 0d00                      | move-exception v0
      0x0005: 2001 4908                 | instance-of v1, v0, java.lang.RuntimeException // type@2121
      0x0007: 3801 fcff                 | if-eqz v1, -4
      0x0009: 2201 2008                 | new-instance v1, java.io.IOException // type@2080
      0x000b: 7010 ca41 0100            | invoke-direct {v1}, void java.io.IOException.<init>() // method@16842
      0x000e: 2701                      | throw v1
    OatMethodOffsets (offset=0x002778dc)
      code_offset: 0x0066341c 
      gc_map: (offset=0x002ce33a)
    OatQuickMethodHeader (offset=0x00663400)
      mapping_table: (offset=0x00308696)
      vmap_table: (offset=0x0030e0fa)
      v65535/r30
    QuickMethodFrameInfo
      frame_size_in_bytes: 48
      core_spill_mask: 0x40000000 (r30)
      fp_spill_mask: 0x00000000 
      vr_stack_locations:
        locals: v0[sp + #28] v1[sp + #32]
        ins: v2[sp + #56]
        method*: v3[sp + #0]
        outs: v0[sp + #8]
    CODE: (code_offset=0x0066341c size_offset=0x00663418 size=288)...
      0x0066341c: d1400bf0  sub x16, sp, #0x2000 (8192)
      0x00663420: b940021f  ldr wzr, [x16]
      suspend point dex PC: 0x0000
      GC map objects:  v2 ([sp + #56])
      0x00663424: f81d0fe0  str x0, [sp, #-48]!
      0x00663428: f90017fe  str lr, [sp, #40]
      0x0066342c: b9003be1  str w1, [sp, #56]
      0x00663430: 79400250  ldrh w16, [tr] (state_and_flags)
      0x00663434: 350006b0  cbnz w16, #+0xd4 (addr 0x663508)

本地变量:
v0是sp+28
v1是sp+32

开始先将sp+56(v2)清0.
然后调用我们自己写的withException方法。

      0x00663438: b9403be0  ldr w0, [sp, #56]
      0x0066343c: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x0000
      GC map objects:  v2 ([sp + #56])
      0x00663440: b9001be0  str w0, [sp, #24]
      0x00663444: b9401be1  ldr w1, [sp, #24]
      0x00663448: b9400020  ldr w0, [x1]
      0x0066344c: f9418c00  ldr x0, [x0, #792]
      0x00663450: f940181e  ldr lr, [x0, #48]
      0x00663454: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      GC map objects:  v2 ([sp + #56])

返回值也用不到,所以直接返回了。

      0x00663458: f94017fe  ldr lr, [sp, #40]
      0x0066345c: 9100c3ff  add sp, sp, #0x30 (48)
      0x00663460: d65f03c0  ret
      catch entry dex PC: 0x0004

下面是处理Exception的部分:

Exception的类型放到w0中,然后去判断类型是否已经初始化过了。

      0x00663464: b9408a40  ldr w0, [tr, #136] (exception)
      0x00663468: b9008a5f  str wzr, [tr, #136] (exception)
      0x0066346c: b9001fe0  str w0, [sp, #28]
      0x00663470: f94003e0  ldr x0, [sp]
      0x00663474: b9400800  ldr w0, [x0, #8]
      0x00663478: b9613000  ldr w0, [x0, #8496]
      0x0066347c: 340004c0  cbz w0, #+0x98 (addr 0x663514)

如果没有初始化,那就真接跳0x663490,返回

      0x00663480: b9001be0  str w0, [sp, #24]
      0x00663484: b9401fe0  ldr w0, [sp, #28]
      0x00663488: b9401be1  ldr w1, [sp, #24]
      0x0066348c: 52800002  mov w2, #0x0
      0x00663490: 340000a0  cbz w0, #+0x14 (addr 0x6634a4)

对象已经初始化过了,就去调pInstanceofNonTrivial去做instanceof检查。

      0x00663494: b9400002  ldr w2, [x0]
      0x00663498: 6b01005f  cmp w2, w1
      0x0066349c: 54000441  b.ne #+0x88 (addr 0x663524)
      0x006634a0: 52800022  mov w2, #0x1

下面这段是判断不是RuntimeException的子类,所以跳回,结束的情况。0x00663458是上面的返回部分的代码。

      0x006634a4: b90023e2  str w2, [sp, #32]
      0x006634a8: b94023e0  ldr w0, [sp, #32]
      0x006634ac: 7100001f  cmp w0, #0x0 (0)
      0x006634b0: 1a9f17e1  cset w1, eq
      0x006634b4: 2a0103e0  mov w0, w1
      0x006634b8: 35fffd00  cbnz w0, #-0x60 (addr 0x663458)

这里调用pAllocObject去new IOException的对象。

      0x006634bc: f94003e1  ldr x1, [sp]
      0x006634c0: 52810400  mov w0, #0x820
      0x006634c4: f940d65e  ldr lr, [tr, #424] (pAllocObject)
      0x006634c8: d63f03c0  blr lr
      suspend point dex PC: 0x0009
      GC map objects:  v0 ([sp + #28]), v2 ([sp + #56])
      0x006634cc: b90023e0  str w0, [sp, #32]
      0x006634d0: b94023e0  ldr w0, [sp, #32]
      0x006634d4: b940001f  ldr wzr, [x0]
      suspend point dex PC: 0x000b
      GC map objects:  v0 ([sp + #28]), v1 ([sp + #32]), v2 ([sp + #56])

下面当然是调IOException的构造方法了:

      0x006634d8: b9001be0  str w0, [sp, #24]
      0x006634dc: b9401be1  ldr w1, [sp, #24]
      0x006634e0: f94003e0  ldr x0, [sp]
      0x006634e4: b9400400  ldr w0, [x0, #4]
      0x006634e8: d281cc10  mov x16, #0xe60
      0x006634ec: f2a00050  movk x16, #0x2, lsl #16
      0x006634f0: f8706800  ldr x0, [x0, x16]
      0x006634f4: f940181e  ldr lr, [x0, #48]
      0x006634f8: d63f03c0  blr lr
      suspend point dex PC: 0x000b
      GC map objects:  v0 ([sp + #28]), v1 ([sp + #32]), v2 ([sp + #56])

最后调用pDeliveryException抛出去。

      0x006634fc: b94023e0  ldr w0, [sp, #32]
      0x00663500: f942225e  ldr lr, [tr, #1088] (pDeliverException)
      0x00663504: d63f03c0  blr lr
      suspend point dex PC: 0x000e
      GC map objects:  v0 ([sp + #28]), v1 ([sp + #32]), v2 ([sp + #56])
      0x00663508: f9421e5e  ldr lr, [tr, #1080] (pTestSuspend)
      0x0066350c: d63f03c0  blr lr
      suspend point dex PC: 0x0000
      GC map objects:  v2 ([sp + #56])
      0x00663510: 17ffffca  b #-0xd8 (addr 0x663438)

pInitializeType是检查类型是否已经初始化过了。

      0x00663514: 52810920  mov w0, #0x849
      0x00663518: f9410a5e  ldr lr, [tr, #528] (pInitializeType)
      0x0066351c: d63f03c0  blr lr
      suspend point dex PC: 0x0005
      GC map objects:  v0 ([sp + #28]), v2 ([sp + #56])
      0x00663520: 17ffffd8  b #-0xa0 (addr 0x663480)

最后是instanceof的实现,pInstanceofNonTrivial

      0x00663524: aa0103e0  mov x0, x1
      0x00663528: aa0203e1  mov x1, x2
      0x0066352c: f940fa5e  ldr lr, [tr, #496] (pInstanceofNonTrivial)
      0x00663530: d63f03c0  blr lr
      suspend point dex PC: 0x0005
      GC map objects:  v0 ([sp + #28]), v2 ([sp + #56])
      0x00663534: 2a0003e2  mov w2, w0
      0x00663538: 17ffffdb  b #-0x94 (addr 0x6634a4)

异常处理相关指令总结

Java字节码 Dalvik指令 OAT过程
athrow throw v0 pDeliverException
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,623评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 通俗编程——白话JAVA异常机制 - 代码之道,编程之法 - 博客频道 - CSDN.NEThttp://blog...
    葡萄喃喃呓语阅读 3,179评论 0 25
  • ART世界探险(8) - 面向对象编程 对象和方法调用 接口定义: 给接口做一个实现: 我们先做一个新建对象,并调...
    Jtag特工阅读 487评论 0 1
  • 一. Java基础部分.................................................
    wy_sure阅读 3,810评论 0 11