Skip to content

Commit 82b840b

Browse files
mtdowlingadwsingh
authored andcommitted
Use rolling bytecode version, address PR
Instead of a major.minor version in the bytecode, we can just use a simpler approach of a rolling bytecode version, and more simply the version of the implementation has to be >= the version in the loaded bytecode.
1 parent cf1addd commit 82b840b

7 files changed

Lines changed: 64 additions & 24 deletions

File tree

client/client-core/src/main/java/software/amazon/smithy/java/client/core/endpoint/EndpointImpl.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.net.URI;
99
import java.net.URISyntaxException;
1010
import java.util.ArrayList;
11+
import java.util.Collections;
1112
import java.util.HashMap;
1213
import java.util.List;
1314
import java.util.Map;
@@ -23,8 +24,8 @@ final class EndpointImpl implements Endpoint {
2324

2425
private EndpointImpl(Builder builder) {
2526
this.uri = Objects.requireNonNull(builder.uri);
26-
this.authSchemes = builder.authSchemes == null ? List.of() : builder.authSchemes;
27-
this.properties = builder.properties == null ? Map.of() : builder.properties;
27+
this.authSchemes = builder.authSchemes == null ? List.of() : Collections.unmodifiableList(builder.authSchemes);
28+
this.properties = builder.properties == null ? Map.of() : Collections.unmodifiableMap(builder.properties);
2829
// Clear out the builder, making this class immutable and the builder still reusable.
2930
builder.authSchemes = null;
3031
builder.properties = null;

client/client-rulesengine/src/main/java/software/amazon/smithy/java/client/rulesengine/Bytecode.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* Offset Size Description
3131
* ------ ---- -----------
3232
* 0 4 Magic number (0x52554C45 = "RULE")
33-
* 4 2 Version (major.minor, currently 0x0101 = 1.1)
33+
* 4 2 Version (rolling version number, currently 1)
3434
* 6 2 Condition count (unsigned short)
3535
* 8 2 Result count (unsigned short)
3636
* 10 2 Register count (unsigned short)
@@ -152,14 +152,15 @@
152152
public final class Bytecode {
153153

154154
static final int MAGIC = 0x52554C45; // "RULE"
155-
static final short VERSION = 0x0101; // 1.1
155+
static final short VERSION = 1;
156156
static final byte CONST_NULL = 0;
157157
static final byte CONST_STRING = 1;
158158
static final byte CONST_INTEGER = 2;
159159
static final byte CONST_BOOLEAN = 3;
160160
static final byte CONST_LIST = 4;
161161
static final byte CONST_MAP = 5;
162162

163+
private final short version;
163164
private final byte[] bytecode;
164165
private final int[] conditionOffsets;
165166
private final int[] resultOffsets;
@@ -187,6 +188,28 @@ public final class Bytecode {
187188
RulesFunction[] functions,
188189
int[] bddNodes,
189190
int bddRootRef
191+
) {
192+
this(bytecode,
193+
conditionOffsets,
194+
resultOffsets,
195+
registerDefinitions,
196+
constantPool,
197+
functions,
198+
bddNodes,
199+
bddRootRef,
200+
VERSION);
201+
}
202+
203+
Bytecode(
204+
byte[] bytecode,
205+
int[] conditionOffsets,
206+
int[] resultOffsets,
207+
RegisterDefinition[] registerDefinitions,
208+
Object[] constantPool,
209+
RulesFunction[] functions,
210+
int[] bddNodes,
211+
int bddRootRef,
212+
short version
190213
) {
191214
if (bddNodes.length % 3 != 0) {
192215
throw new IllegalArgumentException("BDD nodes length must be multiple of 3, got: " + bddNodes.length);
@@ -205,6 +228,16 @@ public final class Bytecode {
205228
this.builtinIndices = findBuiltinIndices(registerDefinitions);
206229
this.hardRequiredIndices = findRequiredIndicesWithoutDefaultsOrBuiltins(registerDefinitions);
207230
this.inputRegisterMap = createInputRegisterMap(registerDefinitions);
231+
this.version = version;
232+
}
233+
234+
/**
235+
* Get the bytecode version.
236+
*
237+
* @return bytecode version number.
238+
*/
239+
public short getVersion() {
240+
return version;
208241
}
209242

210243
/**

client/client-rulesengine/src/main/java/software/amazon/smithy/java/client/rulesengine/BytecodeDisassembler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ String disassemble() {
139139
StringBuilder s = new StringBuilder();
140140

141141
s.append("=== Bytecode Program ===\n");
142+
s.append("Version: ").append(bytecode.getVersion()).append("\n");
142143
s.append("Conditions: ").append(bytecode.getConditionCount()).append("\n");
143144
s.append("Results: ").append(bytecode.getResultCount()).append("\n");
144145
s.append("Registers: ").append(bytecode.getRegisterDefinitions().length).append("\n");

client/client-rulesengine/src/main/java/software/amazon/smithy/java/client/rulesengine/BytecodeReader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ short readShort() {
4040
return (short) value;
4141
}
4242

43+
int readUnsignedShort() {
44+
return readShort() & 0xFFFF;
45+
}
46+
4347
int readInt() {
4448
checkBounds(4);
4549
int value = (data[offset] & 0xFF) << 24;

client/client-rulesengine/src/main/java/software/amazon/smithy/java/client/rulesengine/BytecodeWriter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* Builds up bytecode incrementally.
1919
*/
2020
final class BytecodeWriter {
21-
private static final int MAX_CONSTANTS = 65536;
21+
private static final int MAX_CONSTANTS = 0xFFFF + 1;
2222

2323
private final ByteArrayOutputStream bytecodeStream = new ByteArrayOutputStream();
2424
private final List<Integer> conditionOffsets = new ArrayList<>();
@@ -45,7 +45,7 @@ void writeByte(int value) {
4545
}
4646

4747
void writeShort(int value) {
48-
if (value < 0 || value > 65535) {
48+
if (value < 0 || value > 0xFFFF) {
4949
throw new IllegalArgumentException("Value out of range for unsigned short: " + value);
5050
}
5151
bytecodeStream.write((value >> 8) & 0xFF);

client/client-rulesengine/src/main/java/software/amazon/smithy/java/client/rulesengine/RegisterFiller.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ static RegisterFiller of(Bytecode bytecode, Map<String, Function<Context, Object
108108
}
109109
}
110110

111-
// Fast implementation for <= 64 registers using single long bitmasks.
111+
// Fast implementation for < 64 registers using single long bitmasks.
112112
private static final class FastRegisterFiller extends RegisterFiller {
113113
private final long builtinMask;
114114
private final long requiredMask;

client/client-rulesengine/src/main/java/software/amazon/smithy/java/client/rulesengine/RulesEngineBuilder.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public final class RulesEngineBuilder {
3434
}
3535
}
3636

37+
private static final int BYTECODE_HEADER_SIZE = Integer.BYTES // magic number
38+
+ Short.BYTES // version
39+
+ (5 * Short.BYTES) // counts
40+
+ (2 * Integer.BYTES) // bddNodeCount, bddRootRef
41+
+ (5 * Integer.BYTES); // offsets
42+
3743
private final List<RulesExtension> extensions = new ArrayList<>();
3844
private final Map<String, RulesFunction> functions = new LinkedHashMap<>();
3945
private final Map<String, Function<Context, Object>> builtinProviders = new HashMap<>();
@@ -138,25 +144,19 @@ public Bytecode load(byte[] data) {
138144
}
139145

140146
short version = reader.readShort();
141-
if (version != Bytecode.VERSION) {
142-
int major = (version >> 8) & 0xFF;
143-
int minor = version & 0xFF;
144-
int expectedMajor = (Bytecode.VERSION >> 8) & 0xFF;
145-
int expectedMinor = Bytecode.VERSION & 0xFF;
147+
if (version > Bytecode.VERSION) {
146148
throw new IllegalArgumentException(String.format(
147-
"Unsupported bytecode version: %d.%d (expected %d.%d)",
148-
major,
149-
minor,
150-
expectedMajor,
151-
expectedMinor));
149+
"Unsupported bytecode version: %d (maximum supported: %d)",
150+
version,
151+
Bytecode.VERSION));
152152
}
153153

154154
// Read counts
155-
int conditionCount = reader.readShort() & 0xFFFF;
156-
int resultCount = reader.readShort() & 0xFFFF;
157-
int registerCount = reader.readShort() & 0xFFFF;
158-
int constantCount = reader.readShort() & 0xFFFF;
159-
int functionCount = reader.readShort() & 0xFFFF;
155+
int conditionCount = reader.readUnsignedShort();
156+
int resultCount = reader.readUnsignedShort();
157+
int registerCount = reader.readUnsignedShort();
158+
int constantCount = reader.readUnsignedShort();
159+
int functionCount = reader.readUnsignedShort();
160160
int bddNodeCount = reader.readInt();
161161
int bddRootRef = reader.readInt();
162162

@@ -172,7 +172,7 @@ public Bytecode load(byte[] data) {
172172
int bddTableOffset = reader.readInt();
173173

174174
// Validate offsets are within bounds and in expected order
175-
if (conditionTableOffset < 44
175+
if (conditionTableOffset < BYTECODE_HEADER_SIZE
176176
|| conditionTableOffset > data.length
177177
|| resultTableOffset < conditionTableOffset
178178
|| resultTableOffset > data.length
@@ -253,7 +253,8 @@ public Bytecode load(byte[] data) {
253253
constantPool,
254254
resolvedFunctions,
255255
bddNodes,
256-
bddRootRef);
256+
bddRootRef,
257+
version);
257258
}
258259

259260
private RulesFunction[] loadFunctions(BytecodeReader reader, int count) {

0 commit comments

Comments
 (0)