8
8
import java .net .URI ;
9
9
import java .net .URISyntaxException ;
10
10
import java .util .Arrays ;
11
+ import java .util .Collections ;
11
12
import java .util .HashMap ;
12
13
import java .util .LinkedHashMap ;
13
14
import java .util .List ;
@@ -36,8 +37,8 @@ protected boolean removeEldestEntry(Map.Entry<String, URI> eldest) {
36
37
private static final int MIN_TEMP_ARRAY_SIZE = 8 ;
37
38
38
39
// Temp array used during evaluation.
39
- private Object [] tempArray ;
40
- private int tempArraySize = 0 ;
40
+ private Object [] tempArray = new Object [ 8 ] ;
41
+ private int tempArraySize = 8 ;
41
42
42
43
private final Context context ;
43
44
private final RulesProgram program ;
@@ -109,14 +110,18 @@ void initializeRegister(Context context, int index, ParamDefinition definition)
109
110
110
111
private void push (Object value ) {
111
112
if (stackPosition == stack .length ) {
112
- int newCapacity = stack .length + (stack .length >> 1 );
113
- Object [] newStack = new Object [newCapacity ];
114
- System .arraycopy (stack , 0 , newStack , 0 , stack .length );
115
- stack = newStack ;
113
+ resizeStack ();
116
114
}
117
115
stack [stackPosition ++] = value ;
118
116
}
119
117
118
+ private void resizeStack () {
119
+ int newCapacity = stack .length + (stack .length >> 1 );
120
+ Object [] newStack = new Object [newCapacity ];
121
+ System .arraycopy (stack , 0 , newStack , 0 , stack .length );
122
+ stack = newStack ;
123
+ }
124
+
120
125
private Object pop () {
121
126
return stack [--stackPosition ]; // no need to clear out the memory since it's tied to lifetime of the VM.
122
127
}
@@ -139,22 +144,13 @@ private Object run() {
139
144
// Skip version, params, and register bytes.
140
145
for (pc = program .instructionOffset + 3 ; pc < instructionSize ; pc ++) {
141
146
switch (instructions [pc ]) {
142
- case RulesProgram .LOAD_CONST -> {
143
- int constant = instructions [++pc ] & 0xFF ; // read unsigned byte
144
- push (constantPool [constant ]);
145
- }
147
+ case RulesProgram .LOAD_CONST -> push (constantPool [instructions [++pc ] & 0xFF ]); // read unsigned byte
146
148
case RulesProgram .LOAD_CONST_W -> {
147
149
push (constantPool [readUnsignedShort (pc + 1 )]); // read unsigned short
148
150
pc += 2 ;
149
151
}
150
- case RulesProgram .SET_REGISTER -> {
151
- int register = instructions [++pc ] & 0xFF ; // read unsigned byte
152
- registers [register ] = peek ();
153
- }
154
- case RulesProgram .LOAD_REGISTER -> {
155
- int register = instructions [++pc ] & 0xFF ; // read unsigned byte
156
- push (registers [register ]);
157
- }
152
+ case RulesProgram .SET_REGISTER -> registers [instructions [++pc ] & 0xFF ] = peek (); // read unsigned byte
153
+ case RulesProgram .LOAD_REGISTER -> push (registers [instructions [++pc ] & 0xFF ]); // read unsigned byte
158
154
case RulesProgram .JUMP_IF_FALSEY -> {
159
155
Object value = pop ();
160
156
if (value == null || Boolean .FALSE .equals (value )) {
@@ -163,41 +159,26 @@ private Object run() {
163
159
pc += 2 ;
164
160
}
165
161
}
166
- case RulesProgram .NOT -> push (! Boolean .TRUE . equals ( pop ()) );
162
+ case RulesProgram .NOT -> push (pop () != Boolean .TRUE );
167
163
case RulesProgram .ISSET -> {
168
164
Object value = pop ();
169
165
// Push true if it's set and not a boolean, or boolean true.
170
166
push (value != null && !Boolean .FALSE .equals (value ));
171
167
}
172
168
case RulesProgram .TEST_REGISTER_ISSET -> {
173
- int register = instructions [++pc ] & 0xFF ; // read unsigned byte
174
- var value = registers [register ];
169
+ var value = registers [instructions [++pc ] & 0xFF ]; // read unsigned byte
175
170
push (value != null && !Boolean .FALSE .equals (value ));
176
171
}
177
172
case RulesProgram .RETURN_ERROR -> {
178
173
throw new RulesEvaluationError ((String ) pop ());
179
174
}
180
175
case RulesProgram .RETURN_ENDPOINT -> {
181
- short packed = instructions [++pc ];
182
- boolean hasHeaders = (packed & 1 ) != 0 ;
183
- boolean hasProperties = (packed & 2 ) != 0 ;
184
- return setEndpoint (hasProperties , hasHeaders );
185
- }
186
- case RulesProgram .CREATE_LIST -> {
187
- int size = instructions [++pc ] & 0xFF ; // read unsigned byte
188
- var values = new Object [size ];
189
- for (var i = size - 1 ; i >= 0 ; i --) {
190
- values [i ] = pop ();
191
- }
192
- push (Arrays .asList (values ));
193
- }
194
- case RulesProgram .CREATE_MAP -> {
195
- int size = instructions [++pc ] & 0xFF ; // read unsigned byte
196
- createMap (size );
176
+ return setEndpoint (instructions [++pc ]);
197
177
}
178
+ case RulesProgram .CREATE_LIST -> createList (instructions [++pc ] & 0xFF ); // read unsigned byte
179
+ case RulesProgram .CREATE_MAP -> createMap (instructions [++pc ] & 0xFF ); // read unsigned byte
198
180
case RulesProgram .RESOLVE_TEMPLATE -> {
199
- var constant = readUnsignedShort (pc + 1 );
200
- resolveTemplate ((StringTemplate ) constantPool [constant ]);
181
+ resolveTemplate ((StringTemplate ) constantPool [readUnsignedShort (pc + 1 )]);
201
182
pc += 2 ;
202
183
}
203
184
case RulesProgram .FN -> {
@@ -227,10 +208,10 @@ private Object run() {
227
208
push (getAttr .apply (target ));
228
209
pc += 2 ;
229
210
}
230
- case RulesProgram .IS_TRUE -> push (pop () instanceof Boolean b && b );
211
+ case RulesProgram .IS_TRUE -> push (pop () == Boolean . TRUE );
231
212
case RulesProgram .TEST_REGISTER_IS_TRUE -> {
232
213
int register = instructions [++pc ] & 0xFF ; // read unsigned byte
233
- push (registers [register ] instanceof Boolean b && b );
214
+ push (registers [register ] == Boolean . TRUE );
234
215
}
235
216
case RulesProgram .RETURN_VALUE -> {
236
217
return pop ();
@@ -245,17 +226,48 @@ private Object run() {
245
226
}
246
227
247
228
private void createMap (int size ) {
248
- Map <String , Object > headers = new HashMap <>(size );
249
- for (var i = 0 ; i < size ; i ++) {
250
- var value = pop ();
251
- var key = pop ();
252
- headers .put ((String ) key , value );
229
+ push (switch (size ) {
230
+ case 0 -> Map .of ();
231
+ case 1 -> Map .of ((String ) pop (), pop ());
232
+ case 2 -> Map .of ((String ) pop (), pop (), (String ) pop (), pop ());
233
+ case 3 -> Map .of ((String ) pop (), pop (), (String ) pop (), pop (), (String ) pop (), pop ());
234
+ default -> {
235
+ Map <String , Object > map = new HashMap <>((int ) (size / 0.75f ) + 1 ); // Avoid rehashing
236
+ for (var i = 0 ; i < size ; i ++) {
237
+ map .put ((String ) pop (), pop ());
238
+ }
239
+ yield map ;
240
+ }
241
+ });
242
+ }
243
+
244
+ private void createList (int size ) {
245
+ push (switch (size ) {
246
+ case 0 -> List .of ();
247
+ case 1 -> Collections .singletonList (pop ());
248
+ default -> {
249
+ var values = new Object [size ];
250
+ for (var i = size - 1 ; i >= 0 ; i --) {
251
+ values [i ] = pop ();
252
+ }
253
+ yield Arrays .asList (values );
254
+ }
255
+ });
256
+ }
257
+
258
+ private void resolveTemplate (StringTemplate template ) {
259
+ var expressionCount = template .expressionCount ();
260
+ var temp = getTempArray (expressionCount );
261
+ for (var i = 0 ; i < expressionCount ; i ++) {
262
+ temp [i ] = pop ();
253
263
}
254
- push (headers );
264
+ push (template . resolve ( expressionCount , temp ) );
255
265
}
256
266
257
267
@ SuppressWarnings ("unchecked" )
258
- private Endpoint setEndpoint (boolean hasProperties , boolean hasHeaders ) {
268
+ private Endpoint setEndpoint (byte packed ) {
269
+ boolean hasHeaders = (packed & 1 ) != 0 ;
270
+ boolean hasProperties = (packed & 2 ) != 0 ;
259
271
var urlString = (String ) pop ();
260
272
var properties = (Map <String , Object >) (hasProperties ? pop () : Map .of ());
261
273
var headers = (Map <String , List <String >>) (hasHeaders ? pop () : Map .of ());
@@ -286,27 +298,21 @@ public static URI createUri(String uriStr) {
286
298
return uri ;
287
299
}
288
300
289
- private void resolveTemplate (StringTemplate template ) {
290
- if (template .expressionCount () == 0 ) {
291
- push (template .resolve (0 , tempArray ));
292
- } else {
293
- var temp = getTempArray (template .expressionCount ());
294
- for (var i = 0 ; i < template .expressionCount (); i ++) {
295
- temp [i ] = pop ();
296
- }
297
- push (template .resolve (template .expressionCount (), temp ));
301
+ private Object [] getTempArray (int requiredSize ) {
302
+ if (tempArraySize < requiredSize ) {
303
+ resizeTempArray (requiredSize );
298
304
}
305
+ return tempArray ;
299
306
}
300
307
301
- private Object [] getTempArray (int requiredSize ) {
302
- var size = Math .max (MIN_TEMP_ARRAY_SIZE , requiredSize );
303
- if (tempArraySize < size ) {
304
- tempArray = new Object [size ];
305
- tempArraySize = size ;
306
- } else {
307
- // Clear out any previously set values.
308
- Arrays .fill (tempArray , null );
308
+ private void resizeTempArray (int requiredSize ) {
309
+ // Resize to a power of two.
310
+ int newSize = MIN_TEMP_ARRAY_SIZE ;
311
+ while (newSize < requiredSize ) {
312
+ newSize <<= 1 ;
309
313
}
310
- return tempArray ;
314
+
315
+ tempArray = new Object [newSize ];
316
+ tempArraySize = newSize ;
311
317
}
312
318
}
0 commit comments