Что вы можете бросить в Java? - PullRequest
60 голосов
/ 22 апреля 2011

Традиционно считается, что вы можете генерировать только объекты, которые расширяют Throwable в Java, но возможно ли отключить верификатор байт-кода и заставить Java компилировать и запускать код, который генерирует произвольные объекты - или даже примитивы?

Я посмотрел в JVM athrow, и он поместит первый объект в стек операндов; но будет ли он проверять, указывает ли указанная ссылка на Throwable во время выполнения?

Ответы [ 3 ]

72 голосов
/ 22 апреля 2011

Это зависит от вашей реализации JVM. Согласно спецификации Java VM это неопределенное поведение, если объект не Throwable.

Objectref должно иметь ссылку на тип и должно ссылаться на объект, который является экземпляром класса Throwable или подкласса Throwable.

В разделе 6.1, «Значение слова« должен »» :

Если какое-либо ограничение («должен» или «не должен») в описании инструкции не выполняется во время выполнения, поведение виртуальной машины Java не определено.

Я написал тестовую программу, используя Jasmin ассемблер , который эквивалентен throw new Object(). Виртуальная машина Java HotSpot Server выдает VerifyError:

# cat Athrow.j 
.source Athrow.j
.class public Athrow
.super java/lang/Object

.method public <init>()V
    aload_0
    invokenonvirtual java/lang/Object/<init>()V
    return
.end method

.method public static main([Ljava/lang/String;)V
    .limit stack 2

    new java/lang/Object
    dup
    invokenonvirtual java/lang/Object/<init>()V
    athrow

    return
.end method

# java -jar jasmin.jar Athrow.j 
Generated: Athrow.class

# java Athrow
Exception in thread "main" java.lang.VerifyError: (class: Athrow, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects

Отключение верификатора байт-кода позволяет выполнить athrow, и JVM перестает работать, когда он пытается напечатать сведения об исключении. Сравните эти две программы, первая из которых выдает Exception, вторая - это вышеприведенная тестовая программа, которая выдает Object. Обратите внимание, как он выходит в середине распечатки:

# java -Xverify:none examples/Uncaught
Exception in thread "main" java.lang.Exception
        at examples.Uncaught.main(Uncaught.j)
# java -Xverify:none Athrow
Exception in thread "main" #

Конечно, отключение верификатора байт-кода опасно. Собственно виртуальная машина написана так, чтобы предполагать, что проверка байт-кода была выполнена, и, следовательно, нет необходимости проверять операнды инструкций. Осторожно: неопределенное поведение, которое вы вызываете, когда обходите проверку байт-кода, очень похоже на неопределенное поведение в программах на Си; может произойти все что угодно, включая демонов, вылетающих из вашего носа.

7 голосов
/ 10 сентября 2012

Как уже упоминалось в ответе Джона, вы можете отключить проверку (размещение класса в пути загрузки также должно работать), загрузить и выполнить класс, успешно выбрасывающий класс не Throwable.

Удивительно, ноне обязательно приведет к сбою!

Пока вы не вызываете методы Throwable явно или явно, все будет прекрасно работать:

.source ThrowObject.j
.class public ThrowObject
.super java/lang/Object

.method public <init>()V
    aload_0
    invokenonvirtual java/lang/Object/<init>()V
    return
.end method

.method public static main([Ljava/lang/String;)V

    new java/lang/Object
    dup
    invokenonvirtual java/lang/Object/<init>()V

  BeforeThrow:
    athrow

  AfterThrow:

    return

  CatchThrow:

    getstatic java/lang/System/out Ljava/io/PrintStream;
    ldc "Thrown and catched Object successfully!"
    invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
    return

  .catch all from BeforeThrow to AfterThrow using CatchThrow
.end method

Результат:

% java -Xverify:none ThrowObject
Thrown and catched Object successfully!
5 голосов
/ 22 апреля 2011

[...] отключить проверку байт-кода [...]

Проверка байт-кода является частью спецификации JVM, поэтому если вы отключите эту(или вмешиваться в JVM другими способами), вы можете, в зависимости от реализации, делать что угодно (включая бросание примитивов и т. д.).

Цитата из спецификации JVM:

objectref должен иметь ссылку на тип , а должен ссылаться на объект, который является экземпляром класса Throwable или подкласса Throwable.

Т.е. ваш вопрос может быть интерпретирован как "Если JVM отклоняется от спецификации, может ли она делать странные вещи, такие как бросание примитивов" и ответ, конечно, да.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...