8341277: Validate slot argument for instruction factories

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2024-10-02 03:30:02 +00:00
parent 0f381137cb
commit 39c17b3926
15 changed files with 366 additions and 80 deletions

View File

@ -429,6 +429,8 @@ public sealed interface CodeBuilder
* @param tk the load type
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID void}
* or {@code slot} is out of range
* @since 23
*/
default CodeBuilder loadLocal(TypeKind tk, int slot) {
@ -440,6 +442,8 @@ public sealed interface CodeBuilder
* @param tk the store type
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code tk} is {@link TypeKind#VOID void}
* or {@code slot} is out of range
* @since 23
*/
default CodeBuilder storeLocal(TypeKind tk, int slot) {
@ -793,6 +797,7 @@ public sealed interface CodeBuilder
* @param startScope the start scope of the variable
* @param endScope the end scope of the variable
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder localVariable(int slot, Utf8Entry nameEntry, Utf8Entry descriptorEntry, Label startScope, Label endScope) {
return with(LocalVariable.of(slot, nameEntry, descriptorEntry, startScope, endScope));
@ -806,6 +811,7 @@ public sealed interface CodeBuilder
* @param startScope the start scope of the variable
* @param endScope the end scope of the variable
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder localVariable(int slot, String name, ClassDesc descriptor, Label startScope, Label endScope) {
return localVariable(slot,
@ -822,6 +828,7 @@ public sealed interface CodeBuilder
* @param startScope the start scope of the variable
* @param endScope the end scope of the variable
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder localVariableType(int slot, Utf8Entry nameEntry, Utf8Entry signatureEntry, Label startScope, Label endScope) {
return with(LocalVariableType.of(slot, nameEntry, signatureEntry, startScope, endScope));
@ -835,6 +842,7 @@ public sealed interface CodeBuilder
* @param startScope the start scope of the variable
* @param endScope the end scope of the variable
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder localVariableType(int slot, String name, Signature signature, Label startScope, Label endScope) {
return localVariableType(slot,
@ -877,6 +885,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder aload(int slot) {
return loadLocal(TypeKind.REFERENCE, slot);
@ -925,6 +934,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder astore(int slot) {
return storeLocal(TypeKind.REFERENCE, slot);
@ -958,6 +968,7 @@ public sealed interface CodeBuilder
* Generate an instruction pushing an int in the range of byte onto the operand stack.
* @param b the int in the range of byte
* @return this builder
* @throws IllegalArgumentException if {@code b} is out of range of byte
*/
default CodeBuilder bipush(int b) {
return with(ConstantInstruction.ofArgument(Opcode.BIPUSH, b));
@ -1094,6 +1105,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder dload(int slot) {
return loadLocal(TypeKind.DOUBLE, slot);
@ -1139,6 +1151,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder dstore(int slot) {
return storeLocal(TypeKind.DOUBLE, slot);
@ -1306,6 +1319,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder fload(int slot) {
return loadLocal(TypeKind.FLOAT, slot);
@ -1351,6 +1365,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder fstore(int slot) {
return storeLocal(TypeKind.FLOAT, slot);
@ -1726,6 +1741,7 @@ public sealed interface CodeBuilder
* @param slot the local variable slot
* @param val the increment value
* @return this builder
* @throws IllegalArgumentException if {@code slot} or {@code val} is out of range
*/
default CodeBuilder iinc(int slot, int val) {
return with(IncrementInstruction.of(slot, val));
@ -1739,6 +1755,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder iload(int slot) {
return loadLocal(TypeKind.INT, slot);
@ -1997,6 +2014,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder istore(int slot) {
return storeLocal(TypeKind.INT, slot);
@ -2159,6 +2177,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder lload(int slot) {
return loadLocal(TypeKind.LONG, slot);
@ -2228,6 +2247,7 @@ public sealed interface CodeBuilder
*
* @param slot the local variable slot
* @return this builder
* @throws IllegalArgumentException if {@code slot} is out of range
*/
default CodeBuilder lstore(int slot) {
return storeLocal(TypeKind.LONG, slot);
@ -2278,6 +2298,7 @@ public sealed interface CodeBuilder
* @param array the array type
* @param dims the number of dimensions
* @return this builder
* @throws IllegalArgumentException if {@code dims} is out of range
*/
default CodeBuilder multianewarray(ClassEntry array, int dims) {
return with(NewMultiArrayInstruction.of(array, dims));
@ -2289,6 +2310,7 @@ public sealed interface CodeBuilder
* @param dims the number of dimensions
* @return this builder
* @throws IllegalArgumentException if {@code array} represents a primitive type
* or if {@code dims} is out of range
*/
default CodeBuilder multianewarray(ClassDesc array, int dims) {
return multianewarray(constantPool().classEntry(array), dims);
@ -2327,6 +2349,8 @@ public sealed interface CodeBuilder
* Generate an instruction to create a new array of a primitive type
* @param typeKind the primitive array type
* @return this builder
* @throws IllegalArgumentException when the {@code typeKind} is not a legal
* primitive array component type
*/
default CodeBuilder newarray(TypeKind typeKind) {
return with(NewPrimitiveArrayInstruction.of(typeKind));
@ -2423,6 +2447,7 @@ public sealed interface CodeBuilder
* Generate an instruction pushing an int in the range of short onto the operand stack.
* @param s the int in the range of short
* @return this builder
* @throws IllegalArgumentException if {@code s} is out of range of short
*/
default CodeBuilder sipush(int s) {
return with(ConstantInstruction.ofArgument(Opcode.SIPUSH, s));

View File

@ -30,6 +30,7 @@ import java.lang.classfile.Instruction;
import java.lang.classfile.Label;
import java.lang.classfile.Opcode;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.BytecodeHelpers;
import jdk.internal.classfile.impl.Util;
import jdk.internal.javac.PreviewFeature;
@ -112,10 +113,10 @@ public sealed interface DiscontinuedInstruction extends Instruction {
* which must be of kind {@link Opcode.Kind#DISCONTINUED_RET}
* @param slot the local variable slot to load return address from
* @throws IllegalArgumentException if the opcode kind is not
* {@link Opcode.Kind#DISCONTINUED_RET}.
* {@link Opcode.Kind#DISCONTINUED_RET} or if {@code slot} is out of range
*/
static RetInstruction of(Opcode op, int slot) {
Util.checkKind(op, Opcode.Kind.DISCONTINUED_RET);
BytecodeHelpers.validateRet(op, slot);
return new AbstractInstruction.UnboundRetInstruction(op, slot);
}
@ -123,6 +124,7 @@ public sealed interface DiscontinuedInstruction extends Instruction {
* {@return a RET instruction}
*
* @param slot the local variable slot to load return address from
* @throws IllegalArgumentException if {@code slot} is out of range
*/
static RetInstruction of(int slot) {
return of(slot < 256 ? Opcode.RET : Opcode.RET_W, slot);

View File

@ -58,6 +58,7 @@ public sealed interface IncrementInstruction extends Instruction
*
* @param slot the local variable slot to increment
* @param constant the value to increment by
* @throws IllegalArgumentException if {@code slot} or {@code constant} is out of range
*/
static IncrementInstruction of(int slot, int constant) {
return new AbstractInstruction.UnboundIncrementInstruction(slot, constant);

View File

@ -62,9 +62,12 @@ public sealed interface LoadInstruction extends Instruction
*
* @param kind the type of the value to be loaded
* @param slot the local variable slot to load from
* @throws IllegalArgumentException if {@code kind} is
* {@link TypeKind#VOID void} or {@code slot} is out of range
*/
static LoadInstruction of(TypeKind kind, int slot) {
return of(BytecodeHelpers.loadOpcode(kind, slot), slot);
var opcode = BytecodeHelpers.loadOpcode(kind, slot); // validates slot, trusted
return new AbstractInstruction.UnboundLoadInstruction(opcode, slot);
}
/**
@ -74,10 +77,11 @@ public sealed interface LoadInstruction extends Instruction
* which must be of kind {@link Opcode.Kind#LOAD}
* @param slot the local variable slot to load from
* @throws IllegalArgumentException if the opcode kind is not
* {@link Opcode.Kind#LOAD}.
* {@link Opcode.Kind#LOAD} or {@code slot} is out of range
*/
static LoadInstruction of(Opcode op, int slot) {
Util.checkKind(op, Opcode.Kind.LOAD);
BytecodeHelpers.validateSlot(op, slot, true);
return new AbstractInstruction.UnboundLoadInstruction(op, slot);
}
}

View File

@ -92,6 +92,7 @@ public sealed interface LocalVariable extends PseudoInstruction
* @param descriptorEntry the local variable descriptor
* @param startScope the start range of the local variable scope
* @param endScope the end range of the local variable scope
* @throws IllegalArgumentException if {@code slot} is out of range
*/
static LocalVariable of(int slot, Utf8Entry nameEntry, Utf8Entry descriptorEntry, Label startScope, Label endScope) {
return new AbstractPseudoInstruction.UnboundLocalVariable(slot, nameEntry, descriptorEntry,
@ -106,6 +107,7 @@ public sealed interface LocalVariable extends PseudoInstruction
* @param descriptor the local variable descriptor
* @param startScope the start range of the local variable scope
* @param endScope the end range of the local variable scope
* @throws IllegalArgumentException if {@code slot} is out of range
*/
static LocalVariable of(int slot, String name, ClassDesc descriptor, Label startScope, Label endScope) {
return of(slot,

View File

@ -89,6 +89,7 @@ public sealed interface LocalVariableType extends PseudoInstruction
* @param signatureEntry the local variable signature
* @param startScope the start range of the local variable scope
* @param endScope the end range of the local variable scope
* @throws IllegalArgumentException if {@code slot} is out of range
*/
static LocalVariableType of(int slot, Utf8Entry nameEntry, Utf8Entry signatureEntry, Label startScope, Label endScope) {
return new AbstractPseudoInstruction.UnboundLocalVariableType(slot, nameEntry, signatureEntry,
@ -103,6 +104,7 @@ public sealed interface LocalVariableType extends PseudoInstruction
* @param signature the local variable signature
* @param startScope the start range of the local variable scope
* @param endScope the end range of the local variable scope
* @throws IllegalArgumentException if {@code slot} is out of range
*/
static LocalVariableType of(int slot, String name, Signature signature, Label startScope, Label endScope) {
return of(slot,

View File

@ -29,6 +29,7 @@ import java.lang.classfile.CodeModel;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.Instruction;
import jdk.internal.classfile.impl.AbstractInstruction;
import jdk.internal.classfile.impl.BytecodeHelpers;
import jdk.internal.javac.PreviewFeature;
/**
@ -58,9 +59,11 @@ public sealed interface NewMultiArrayInstruction extends Instruction
*
* @param arrayTypeEntry the type of the array
* @param dimensions the number of dimensions of the array
* @throws IllegalArgumentException if {@code dimensions} is out of range
*/
static NewMultiArrayInstruction of(ClassEntry arrayTypeEntry,
int dimensions) {
BytecodeHelpers.validateMultiArrayDimensions(dimensions);
return new AbstractInstruction.UnboundNewMultidimensionalArrayInstruction(arrayTypeEntry, dimensions);
}
}

View File

@ -61,9 +61,12 @@ public sealed interface StoreInstruction extends Instruction
*
* @param kind the type of the value to be stored
* @param slot the local variable slot to store to
* @throws IllegalArgumentException if {@code kind} is {@link
* TypeKind#VOID void} or {@code slot} is out of range
*/
static StoreInstruction of(TypeKind kind, int slot) {
return of(BytecodeHelpers.storeOpcode(kind, slot), slot);
var opcode = BytecodeHelpers.storeOpcode(kind, slot); // validates slot
return new AbstractInstruction.UnboundStoreInstruction(opcode, slot);
}
/**
@ -73,10 +76,11 @@ public sealed interface StoreInstruction extends Instruction
* which must be of kind {@link Opcode.Kind#STORE}
* @param slot the local variable slot to store to
* @throws IllegalArgumentException if the opcode kind is not
* {@link Opcode.Kind#STORE}.
* {@link Opcode.Kind#STORE} or {@code slot} is out of range
*/
static StoreInstruction of(Opcode op, int slot) {
Util.checkKind(op, Opcode.Kind.STORE);
BytecodeHelpers.validateSlot(op, slot, false);
return new AbstractInstruction.UnboundStoreInstruction(op, slot);
}
}

View File

@ -848,9 +848,9 @@ public abstract sealed class AbstractInstruction
final int constant;
public UnboundIncrementInstruction(int slot, int constant) {
super(slot <= 255 && constant < 128 && constant > -127
? Opcode.IINC
: Opcode.IINC_W);
super(BytecodeHelpers.validateAndIsWideIinc(slot, constant)
? Opcode.IINC_W
: Opcode.IINC);
this.slot = slot;
this.constant = constant;
}
@ -867,7 +867,7 @@ public abstract sealed class AbstractInstruction
@Override
public void writeTo(DirectCodeBuilder writer) {
writer.writeIncrement(slot, constant);
writer.writeIncrement(op == Opcode.IINC_W, slot, constant);
}
@Override

View File

@ -163,6 +163,7 @@ public abstract sealed class AbstractPseudoInstruction
protected final Label endScope;
public AbstractLocalPseudo(int slot, Utf8Entry name, Utf8Entry descriptor, Label startScope, Label endScope) {
BytecodeHelpers.validateSlot(slot);
this.slot = slot;
this.name = name;
this.descriptor = descriptor;

View File

@ -45,6 +45,7 @@ import java.lang.classfile.constantpool.LoadableConstantEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.classfile.constantpool.MethodHandleEntry;
import java.lang.classfile.constantpool.NameAndTypeEntry;
import java.util.Objects;
import static jdk.internal.classfile.impl.RawBytecodeHelper.*;
@ -60,6 +61,14 @@ public class BytecodeHelpers {
return new IllegalArgumentException(String.format("convert %s -> %s", from, to));
}
public static IllegalArgumentException slotOutOfBounds(int slot) {
return new IllegalArgumentException("Invalid slot index :".concat(Integer.toString(slot)));
}
public static IllegalArgumentException slotOutOfBounds(Opcode opcode, int slot) {
return new IllegalArgumentException("Invalid slot index %d for %s".formatted(slot, opcode));
}
public static Opcode loadOpcode(TypeKind tk, int slot) {
return switch (tk) {
case INT, SHORT, BYTE, CHAR, BOOLEAN
@ -78,7 +87,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.ALOAD_1;
case 2 -> Opcode.ALOAD_2;
case 3 -> Opcode.ALOAD_3;
default -> (slot < 256) ? Opcode.ALOAD : Opcode.ALOAD_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.ALOAD;
if ((slot & 0xFFFF) == slot)
yield Opcode.ALOAD_W;
throw slotOutOfBounds(slot);
}
};
}
@ -88,7 +103,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.FLOAD_1;
case 2 -> Opcode.FLOAD_2;
case 3 -> Opcode.FLOAD_3;
default -> (slot < 256) ? Opcode.FLOAD : Opcode.FLOAD_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.FLOAD;
if ((slot & 0xFFFF) == slot)
yield Opcode.FLOAD_W;
throw slotOutOfBounds(slot);
}
};
}
@ -98,7 +119,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.DLOAD_1;
case 2 -> Opcode.DLOAD_2;
case 3 -> Opcode.DLOAD_3;
default -> (slot < 256) ? Opcode.DLOAD : Opcode.DLOAD_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.DLOAD;
if ((slot & 0xFFFF) == slot)
yield Opcode.DLOAD_W;
throw slotOutOfBounds(slot);
}
};
}
@ -108,7 +135,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.LLOAD_1;
case 2 -> Opcode.LLOAD_2;
case 3 -> Opcode.LLOAD_3;
default -> (slot < 256) ? Opcode.LLOAD : Opcode.LLOAD_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.LLOAD;
if ((slot & 0xFFFF) == slot)
yield Opcode.LLOAD_W;
throw slotOutOfBounds(slot);
}
};
}
@ -118,7 +151,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.ILOAD_1;
case 2 -> Opcode.ILOAD_2;
case 3 -> Opcode.ILOAD_3;
default -> (slot < 256) ? Opcode.ILOAD : Opcode.ILOAD_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.ILOAD;
if ((slot & 0xFFFF) == slot)
yield Opcode.ILOAD_W;
throw slotOutOfBounds(slot);
}
};
}
@ -140,7 +179,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.ASTORE_1;
case 2 -> Opcode.ASTORE_2;
case 3 -> Opcode.ASTORE_3;
default -> (slot < 256) ? Opcode.ASTORE : Opcode.ASTORE_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.ASTORE;
if ((slot & 0xFFFF) == slot)
yield Opcode.ASTORE_W;
throw slotOutOfBounds(slot);
}
};
}
@ -150,7 +195,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.FSTORE_1;
case 2 -> Opcode.FSTORE_2;
case 3 -> Opcode.FSTORE_3;
default -> (slot < 256) ? Opcode.FSTORE : Opcode.FSTORE_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.FSTORE;
if ((slot & 0xFFFF) == slot)
yield Opcode.FSTORE_W;
throw slotOutOfBounds(slot);
}
};
}
@ -160,7 +211,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.DSTORE_1;
case 2 -> Opcode.DSTORE_2;
case 3 -> Opcode.DSTORE_3;
default -> (slot < 256) ? Opcode.DSTORE : Opcode.DSTORE_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.DSTORE;
if ((slot & 0xFFFF) == slot)
yield Opcode.DSTORE_W;
throw slotOutOfBounds(slot);
}
};
}
@ -170,7 +227,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.LSTORE_1;
case 2 -> Opcode.LSTORE_2;
case 3 -> Opcode.LSTORE_3;
default -> (slot < 256) ? Opcode.LSTORE : Opcode.LSTORE_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.LSTORE;
if ((slot & 0xFFFF) == slot)
yield Opcode.LSTORE_W;
throw slotOutOfBounds(slot);
}
};
}
@ -180,7 +243,13 @@ public class BytecodeHelpers {
case 1 -> Opcode.ISTORE_1;
case 2 -> Opcode.ISTORE_2;
case 3 -> Opcode.ISTORE_3;
default -> (slot < 256) ? Opcode.ISTORE : Opcode.ISTORE_W;
default -> {
if ((slot & 0xFF) == slot)
yield Opcode.ISTORE;
if ((slot & 0xFFFF) == slot)
yield Opcode.ISTORE_W;
throw slotOutOfBounds(slot);
}
};
}
@ -305,6 +374,48 @@ public class BytecodeHelpers {
};
}
public static void validateSlot(Opcode opcode, int slot, boolean load) {
int size = opcode.sizeIfFixed();
if (size == 1 && slot == (load ? intrinsicLoadSlot(opcode) : intrinsicStoreSlot(opcode)) ||
size == 2 && slot == (slot & 0xFF) ||
size == 4 && slot == (slot & 0xFFFF))
return;
throw slotOutOfBounds(opcode, slot);
}
public static void validateSlot(int slot) {
if ((slot & 0xFFFF) != slot)
throw slotOutOfBounds(slot);
}
public static boolean validateAndIsWideIinc(int slot, int val) {
var ret = false;
if ((slot & 0xFF) != slot) {
validateSlot(slot);
ret = true;
}
if ((byte) val != val) {
if ((short) val != val) {
throw new IllegalArgumentException("cannot encode as S2: ".concat(String.valueOf(val)));
}
ret = true;
}
return ret;
}
public static void validateRet(Opcode opcode, int slot) {
if (opcode == Opcode.RET && slot == (slot & 0xFF) ||
opcode == Opcode.RET_W && slot == (slot & 0xFFFF))
return;
Objects.requireNonNull(opcode);
throw slotOutOfBounds(opcode, slot);
}
public static void validateMultiArrayDimensions(int value) {
if (value < 1 || value > 0xFF)
throw new IllegalArgumentException("Not a valid array dimension: ".concat(String.valueOf(value)));
}
public static void validateSipush(int value) {
if (value != (short) value)
throw new IllegalArgumentException(

View File

@ -55,13 +55,13 @@ public final class CodeImpl
case ARRAY_STORE -> ArrayStoreInstruction.of(o);
case CONSTANT -> ConstantInstruction.ofIntrinsic(o);
case CONVERT -> ConvertInstruction.of(o);
case LOAD -> LoadInstruction.of(o, BytecodeHelpers.intrinsicLoadSlot(o));
case LOAD -> new AbstractInstruction.UnboundLoadInstruction(o, BytecodeHelpers.intrinsicLoadSlot(o));
case MONITOR -> MonitorInstruction.of(o);
case NOP -> NopInstruction.of();
case OPERATOR -> OperatorInstruction.of(o);
case RETURN -> ReturnInstruction.of(o);
case STACK -> StackInstruction.of(o);
case STORE -> StoreInstruction.of(o, BytecodeHelpers.intrinsicStoreSlot(o));
case STORE -> new AbstractInstruction.UnboundStoreInstruction(o, BytecodeHelpers.intrinsicStoreSlot(o));
case THROW_EXCEPTION -> ThrowInstruction.of();
default -> throw new AssertionError("invalid opcode: " + o);
};

View File

@ -506,12 +506,12 @@ public final class DirectCodeBuilder
}
}
public void writeIncrement(int slot, int val) {
Opcode opcode = (slot < 256 && val < 128 && val > -127)
? IINC
: IINC_W;
writeBytecode(opcode);
if (opcode.isWide()) {
public void writeIncrement(boolean wide, int slot, int val) {
if (wide) {
bytecodesBufWriter.writeU1(RawBytecodeHelper.WIDE);
}
bytecodesBufWriter.writeU1(RawBytecodeHelper.IINC);
if (wide) {
bytecodesBufWriter.writeU2(slot);
bytecodesBufWriter.writeU2(val);
} else {
@ -1216,7 +1216,7 @@ public final class DirectCodeBuilder
@Override
public CodeBuilder iinc(int slot, int val) {
writeIncrement(slot, val);
writeIncrement(validateAndIsWideIinc(slot, val), slot, val);
return this;
}

View File

@ -0,0 +1,182 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8341277
* @summary Testing ClassFile instruction argument validation.
* @run junit InstructionValidationTest
*/
import java.lang.classfile.*;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.ConstantPoolBuilder;
import java.lang.classfile.instruction.*;
import java.util.List;
import java.util.function.ObjIntConsumer;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import static java.lang.constant.ConstantDescs.*;
import static org.junit.jupiter.api.Assertions.*;
import static java.lang.classfile.Opcode.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
class InstructionValidationTest {
@Test
void testArgumentConstant() {
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, 0));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MIN_VALUE));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MAX_VALUE));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, 0));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MIN_VALUE));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MAX_VALUE));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int) Short.MIN_VALUE - 1));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int) Short.MAX_VALUE + 1));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int) Byte.MIN_VALUE - 1));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int) Byte.MAX_VALUE + 1));
}
/**
* Tests the bad slot argument IAE for load, store, increment, and ret.
*/
@Test
void testSlots() {
record Result(boolean shouldFail, int slot) {
}
List<Integer> badSlots = List.of(-1, 72694, -42, 0x10000, Integer.MIN_VALUE, Integer.MAX_VALUE);
List<Integer> u2OnlySlots = List.of(0x100, 1000, 0xFFFF);
List<Integer> u1Slots = List.of(0, 2, 15, 0xFF);
List<Integer> badU1Slots = Stream.concat(badSlots.stream(), u2OnlySlots.stream()).toList();
List<Integer> u2Slots = Stream.concat(u1Slots.stream(), u2OnlySlots.stream()).toList();
List<Result> u2Cases = Stream.concat(
badSlots.stream().map(i -> new Result(true, i)),
u2Slots.stream().map(i -> new Result(false, i))
).toList();
List<Result> u1Cases = Stream.concat(
badU1Slots.stream().map(i -> new Result(true, i)),
u1Slots.stream().map(i -> new Result(false, i))
).toList();
List<Integer> nonIntrinsicValues = Stream.of(badSlots, u2Slots, u1Slots).<Integer>mapMulti(List::forEach)
.filter(i -> i < 0 || i > 3).toList();
Label[] capture = new Label[1];
ClassFile.of().build(CD_Object, clb -> clb.withMethodBody("test", MTD_void, 0, cob -> {
capture[0] = cob.startLabel();
cob.return_();
}));
Label dummyLabel = capture[0];
List<ObjIntConsumer<CodeBuilder>> cbFactories = List.of(
CodeBuilder::aload,
CodeBuilder::iload,
CodeBuilder::lload,
CodeBuilder::dload,
CodeBuilder::fload,
CodeBuilder::astore,
CodeBuilder::istore,
CodeBuilder::lstore,
CodeBuilder::dstore,
CodeBuilder::fstore
);
for (var r : u2Cases) {
var fails = r.shouldFail;
var i = r.slot;
for (var fac : cbFactories) {
//check(fails, () -> execute(cob -> fac.accept(cob, i)));
}
for (TypeKind tk : TypeKind.values()) {
if (tk == TypeKind.VOID)
continue;
//check(fails, () -> execute(cob -> cob.loadLocal(tk, i)));
//check(fails, () -> execute(cob -> cob.storeLocal(tk, i)));
check(fails, () -> LoadInstruction.of(tk, i));
check(fails, () -> StoreInstruction.of(tk, i));
}
//check(fails, () -> execute(cob -> cob.iinc(i, 1)));
check(fails, () -> IncrementInstruction.of(i, 1));
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(i));
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET_W, i));
check(fails, () -> LocalVariable.of(i, "test", CD_Object, dummyLabel, dummyLabel));
check(fails, () -> LocalVariableType.of(i, "test", Signature.of(CD_Object), dummyLabel, dummyLabel));
}
for (var r : u1Cases) {
var fails = r.shouldFail;
var i = r.slot;
for (var u1Op : List.of(ALOAD, ILOAD, LLOAD, FLOAD, DLOAD))
check(fails, () -> LoadInstruction.of(u1Op, i));
for (var u1Op : List.of(ASTORE, ISTORE, LSTORE, FSTORE, DSTORE))
check(fails, () -> StoreInstruction.of(u1Op, i));
check(fails, () -> DiscontinuedInstruction.RetInstruction.of(RET, i));
}
for (var i : nonIntrinsicValues) {
for (var intrinsicOp : List.of(ALOAD_0, ILOAD_0, LLOAD_0, FLOAD_0, DLOAD_0, ALOAD_1, ILOAD_1, LLOAD_1, FLOAD_1, DLOAD_1,
ALOAD_2, ILOAD_2, LLOAD_2, FLOAD_2, DLOAD_2, ALOAD_3, ILOAD_3, LLOAD_3, FLOAD_3, DLOAD_3)) {
assertThrows(IllegalArgumentException.class, () -> LoadInstruction.of(intrinsicOp, i));
}
for (var intrinsicOp : List.of(ASTORE_0, ISTORE_0, LSTORE_0, FSTORE_0, DSTORE_0, ASTORE_1, ISTORE_1, LSTORE_1, FSTORE_1, DSTORE_1,
ASTORE_2, ISTORE_2, LSTORE_2, FSTORE_2, DSTORE_2, ASTORE_3, ISTORE_3, LSTORE_3, FSTORE_3, DSTORE_3)) {
assertThrows(IllegalArgumentException.class, () -> StoreInstruction.of(intrinsicOp, i));
}
}
}
static void check(boolean fails, Executable exec) {
if (fails) {
assertThrows(IllegalArgumentException.class, exec);
} else {
assertDoesNotThrow(exec);
}
}
@Test
void testIincConstant() {
IncrementInstruction.of(0, 2);
IncrementInstruction.of(0, Short.MAX_VALUE);
IncrementInstruction.of(0, Short.MIN_VALUE);
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, Short.MAX_VALUE + 1));
assertThrows(IllegalArgumentException.class, () -> IncrementInstruction.of(0, Short.MIN_VALUE - 1));
}
@Test
void testNewMultiArrayDimension() {
ClassEntry ce = ConstantPoolBuilder.of().classEntry(CD_Class);
NewMultiArrayInstruction.of(ce, 1);
NewMultiArrayInstruction.of(ce, 13);
NewMultiArrayInstruction.of(ce, 0xFF);
assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, 0));
assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, 0x100));
assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, -1));
assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, Integer.MIN_VALUE));
assertThrows(IllegalArgumentException.class, () -> NewMultiArrayInstruction.of(ce, Integer.MAX_VALUE));
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @summary Testing ClassFile constant instruction argument validation.
* @run junit OpcodesValidationTest
*/
import java.lang.classfile.instruction.ConstantInstruction;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static java.lang.classfile.Opcode.*;
class OpcodesValidationTest {
@Test
void testArgumentConstant() {
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, 0));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MIN_VALUE));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(SIPUSH, Short.MAX_VALUE));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, 0));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MIN_VALUE));
assertDoesNotThrow(() -> ConstantInstruction.ofArgument(BIPUSH, Byte.MAX_VALUE));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int)Short.MIN_VALUE - 1));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(SIPUSH, (int)Short.MAX_VALUE + 1));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int)Byte.MIN_VALUE - 1));
assertThrows(IllegalArgumentException.class, () -> ConstantInstruction.ofArgument(BIPUSH, (int)Byte.MAX_VALUE + 1));
}
}