Class MFrame
- java.lang.Object
-
- xyz.cofe.trambda.bc.mth.MAbstractBC
-
- xyz.cofe.trambda.bc.mth.MFrame
-
- All Implemented Interfaces:
Serializable,xyz.cofe.collection.ImTree<ByteCode>,xyz.cofe.collection.ImTreeWalk<ByteCode>,ByteCode,MethodByteCode,MethodWriter
public class MFrame extends MAbstractBC implements MethodWriter
Java требует проверки всех загруженных классов, чтобы обеспечить безопасность песочницы и обеспечить безопасность кода для оптимизации. Обратите внимание, что это делается на уровне байт-кода , поэтому проверка не проверяет инварианты языка Java, она просто проверяет, что байт-код имеет смысл в соответствии с правилами байт-кода.Помимо прочего, проверка байт-кода гарантирует, что инструкции хорошо сформированы, что все переходы являются допустимыми инструкциями в методе и что все инструкции работают со значениями правильного типа. В последнем случае используется карта стека.
Дело в том, что байт-код сам по себе не содержит явной информации о типе. Типы определяются неявно с помощью анализа потоков данных. Например, инструкция iconst создает целочисленное значение. Если вы храните его в слоте 1, этот слот теперь имеет int. Если поток управления сливается из кода, в котором вместо этого хранится float, слот теперь считается недопустимым типом, что означает, что вы больше ничего не можете сделать с этим значением, пока не перезапишете его.
Исторически верификатор байт-кода выводил все типы, используя эти правила потока данных. К сожалению, невозможно вывести все типы за один линейный проход через байт-код, поскольку обратный переход может привести к недействительности уже выведенных типов. Классический верификатор решил эту проблему, повторяя код до тех пор, пока все не перестанет меняться, что потенциально потребует нескольких проходов.
Однако проверка замедляет загрузку класса в Java. Oracle решил решить эту проблему, добавив новый, более быстрый верификатор, который может проверять байт-код за один проход. Для этого им потребовалось, чтобы все новые классы, начиная с Java 7 (с Java 6 в переходном состоянии), несли метаданные об их типах, чтобы байт-код можно было проверить за один проход. Поскольку сам формат байт-кода не может быть изменен, эта информация о типе хранится отдельно в атрибуте с именем StackMapTable .
Простое хранение типа для каждого отдельного значения в каждой отдельной точке кода, очевидно, заняло бы много места и было бы очень расточительным. Чтобы сделать метаданные меньше и эффективнее, они решили, что в них будут перечислены только типы в позициях, которые являются целями прыжков . Если вы подумаете об этом, это единственный раз, когда вам нужна дополнительная информация для проверки одного прохода. В промежутках между целями прыжка весь поток управления является линейным, поэтому вы можете выводить типы в промежуточных позициях, используя старые правила вывода.
Каждая позиция, в которой явно перечислены типы, называется фреймом карты стека. Атрибут StackMapTable содержит список кадров по порядку, хотя они обычно выражаются как отличие от предыдущего кадра, чтобы уменьшить размер данных. Если в методе нет фреймов, что происходит, когда поток управления никогда не соединяется (т. Е. CFG-это дерево), то атрибут StackMapTable может быть полностью опущен.
Итак, это основная идея о том, как работает StackMapTable и почему он был добавлен. Последний вопрос заключается в том, как создается неявный начальный фрейм. Ответ, конечно, заключается в том, что в начале метода стек операндов пуст, а слоты локальных переменных имеют типы, заданные типами параметров метода, которые определяются из декриптора метода.
Если вы привыкли к Java, есть несколько незначительных различий в том, как типы параметров метода работают на уровне байт-кода. Во-первых, виртуальные методы имеют неявный this в качестве первого параметра. Во - вторых, boolean , byte , char и short не существуют на уровне байт-кода. Вместо этого все они реализуются как внутренние за кулисами.
Фрейм используется для хранения данных и частичных результатов, а также для выполнения динамического связывания, возврата значений для методов и отправки исключений.Новый фрейм создается каждый раз при вызове метода. Кадр уничтожается, когда завершается вызов его метода, независимо от того, является ли это завершение нормальным или внезапным (он вызывает неперехваченное исключение). Фреймы выделяются из стека виртуальной машины Java (§2.5.2) потока, создающего фрейм. Каждый фрейм имеет свой собственный массив локальных переменных (§2.6.1), свой собственный стек операндов (§2.6.2) и ссылку на пул констант времени выполнения (§2.5.5) класса текущего метода. .
Кадр может быть расширен дополнительной информацией, зависящей от реализации, например, отладочной информацией.
Размеры массива локальных переменных и стека операндов определяются во время компиляции и предоставляются вместе с кодом для метода, связанного с кадром (§4.7.3). Таким образом, размер структуры данных кадра зависит только от реализации виртуальной машины Java, и память для этих структур может быть выделена одновременно с вызовом метода.
Только один фрейм, фрейм для выполняемого метода, активен в любой точке данного потока управления. Этот кадр называется текущим кадром, а его метод известен как текущий метод. Класс, в котором определен текущий метод, является текущим классом. Операции с локальными переменными и стеком операндов обычно относятся к текущему кадру.
Фрейм перестает быть текущим, если его метод вызывает другой метод или его метод завершается. Когда вызывается метод, создается новый фрейм, который становится текущим, когда управление передается новому методу. При возврате метода текущий фрейм передает результат своего вызова метода, если таковой имеется, в предыдущий фрейм. Затем текущий кадр отбрасывается, так как предыдущий кадр становится текущим.
Обратите внимание, что кадр, созданный потоком, является локальным для этого потока и не может ссылаться на какой-либо другой поток.
Этот инструкция вставляется непосредственно перед любой инструкцией i, которая следует за инструкцией безусловного перехода, такой как GOTO или THROW,
которая является целью инструкции перехода или запускает блок обработчика исключений. Посещаемые типы должны описывать значения локальных переменных и элементов стека операндов непосредственно перед выполнением i. (*) это обязательно только для классов, версия которых больше или равна Opcodes.V1_6. Кадры метода должны быть указаны либо в развернутой форме, либо в сжатом виде (все кадры должны использовать один и тот же формат, т.е. вы не должны смешивать развернутые и сжатые кадры в одном методе)Visits the current state of the local variables and operand stack elements. This method must(*) be called just before any instruction i that follows an unconditional branch instruction such as GOTO or THROW, that is the target of a jump instruction, or that starts an exception handler block. The visited types must describe the values of the local variables and of the operand stack elements just before i is executed.
(*) this is mandatory only for classes whose version is greater than or equal toOpcodes.V1_6.
The frames of a method must be given either in expanded form, or in compressed form (all frames must use the same format, i.e. you must not mix expanded and compressed frames within a single method):- In expanded form, all frames must have the F_NEW type.
- In compressed form, frames are basically "deltas" from the state of the previous frame:
Opcodes.F_SAMErepresenting frame with exactly the same locals as the previous frame and with the empty stack.Opcodes.F_SAME1representing frame with exactly the same locals as the previous frame and with single value on the stack (numStackis 1 andstack[0]contains value for the type of the stack item).Opcodes.F_APPENDrepresenting frame with current locals are the same as the locals in the previous frame, except that additional locals are defined (numLocalis 1, 2 or 3 andlocalelements contains values representing added types).Opcodes.F_CHOPrepresenting frame with current locals are the same as the locals in the previous frame, except that the last 1-3 locals are absent and with the empty stack (numLocalis 1, 2 or 3).Opcodes.F_FULLrepresenting complete frame data.
In both cases the first frame, corresponding to the method's parameters and access flags, is implicit and must not be visited. Also, it is illegal to visit two or more frames for the same code location (i.e., at least one instruction must be visited between two calls to visitFrame).-
typethe type of this stack map frame. Must beOpcodes.F_NEWfor expanded frames, orOpcodes.F_FULL,Opcodes.F_APPEND,Opcodes.F_CHOP,Opcodes.F_SAMEorOpcodes.F_APPEND,Opcodes.F_SAME1for compressed frames. -
numLocalthe number of local variables in the visited frame. -
localthe local variable types in this frame. This array must not be modified. Primitive types are represented byOpcodes.TOP,Opcodes.INTEGER,Opcodes.FLOAT,Opcodes.LONG,Opcodes.DOUBLE,Opcodes.NULLorOpcodes.UNINITIALIZED_THIS(long and double are represented by a single element). Reference types are represented by String objects (representing internal names), and uninitialized types by Label objects (this label designates the NEW instruction that created this uninitialized value). -
numStackthe number of operand stack elements in the visited frame. -
stackthe operand stack types in this frame. This array must not be modified. Its content has the same format as the "local" array.
- See Also:
- Serialized Form
-
-
Constructor Summary
Constructors Constructor Description MFrame()Конструктор по умолчаниюMFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack)MFrame(int type, int numLocal, List<Object> local, int numStack, List<Object> stack)MFrame(MFrame sample)Конструктор копирования
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description MFrameclone()List<Object>getLocal()intgetNumLocal()intgetNumStack()List<Object>getStack()intgetType()voidsetLocal(List<Object> local)voidsetNumLocal(int numLocal)voidsetNumStack(int numStack)voidsetStack(List<Object> stack)voidsetType(int type)StringtoString()voidwrite(org.objectweb.asm.MethodVisitor mv, MethodWriterCtx ctx)
-
-
-
Constructor Detail
-
MFrame
public MFrame()
Конструктор по умолчанию
-
MFrame
public MFrame(MFrame sample)
Конструктор копирования- Parameters:
sample- образец
-
-
Method Detail
-
clone
public MFrame clone()
- Specified by:
clonein interfaceMethodByteCode- Specified by:
clonein classMAbstractBC
-
getType
public int getType()
-
setType
public void setType(int type)
-
getNumLocal
public int getNumLocal()
-
setNumLocal
public void setNumLocal(int numLocal)
-
getNumStack
public int getNumStack()
-
setNumStack
public void setNumStack(int numStack)
-
write
public void write(org.objectweb.asm.MethodVisitor mv, MethodWriterCtx ctx)- Specified by:
writein interfaceMethodWriter
-
-