for (RowExpression testValue : values) {
ByteCodeNode testByteCode = generatorContext.generate(testValue);
if (testValue instanceof ConstantExpression && ((ConstantExpression) testValue).getValue() != null) {
ConstantExpression constant = (ConstantExpression) testValue;
Object object = constant.getValue();
constantValuesBuilder.add(object);
try {
int hashCode = (int) hashCodeFunction.getCallSite().dynamicInvoker().invoke(object);
hashBucketsBuilder.put(hashCode, testByteCode);
}
catch (Throwable throwable) {
throw new IllegalArgumentException("Error processing IN statement: error calculating hash code for " + object, throwable);
}
}
else {
defaultBucket.add(testByteCode);
}
}
ImmutableListMultimap<Integer, ByteCodeNode> hashBuckets = hashBucketsBuilder.build();
ImmutableSet<Object> constantValues = constantValuesBuilder.build();
LabelNode end = new LabelNode("end");
LabelNode match = new LabelNode("match");
LabelNode noMatch = new LabelNode("noMatch");
LabelNode defaultLabel = new LabelNode("default");
CompilerContext context = generatorContext.getContext();
ByteCodeNode switchBlock;
if (constantValues.size() < 1000) {
Block switchCaseBlocks = new Block(context);
LookupSwitch.LookupSwitchBuilder switchBuilder = lookupSwitchBuilder();
for (Map.Entry<Integer, Collection<ByteCodeNode>> bucket : hashBuckets.asMap().entrySet()) {
LabelNode label = new LabelNode("inHash" + bucket.getKey());
switchBuilder.addCase(bucket.getKey(), label);
Collection<ByteCodeNode> testValues = bucket.getValue();
Block caseBlock = buildInCase(generatorContext, context, type, label, match, defaultLabel, testValues, false);
switchCaseBlocks
.append(caseBlock.setDescription("case " + bucket.getKey()));
}
switchBuilder.defaultCase(defaultLabel);
switchBlock = new Block(context)
.comment("lookupSwitch(hashCode(<stackValue>))")
.dup(javaType)
.invokeDynamic(hashCodeFunction.getName(), hashCodeFunction.getCallSite().type(), hashCodeFunction.getBindingId())
.append(switchBuilder.build())
.append(switchCaseBlocks);
}
else {
// TODO: replace Set with fastutils (or similar) primitive sets if types are primitive
// for huge IN lists, use a Set
FunctionBinding constant = generatorContext.getBootstrapBinder().bindConstant(constantValues, Set.class);
switchBlock = new Block(context)
.comment("inListSet.contains(<stackValue>)")
.append(new IfStatement(context,
new Block(context)
.comment("value (+boxing if necessary)")
.dup(javaType)
.append(ByteCodeUtils.boxPrimitive(context, javaType))
.comment("set")
.invokeDynamic(
constant.getName(),
constant.getCallSite().type(),
constant.getBindingId())
// TODO: use invokeVirtual on the set instead. This requires swapping the two elements in the stack
.invokeStatic(CompilerOperations.class, "in", boolean.class, Object.class, Set.class),
jump(match),
NOP));
}