use of org.jboss.classfilewriter.code.CodeLocation in project undertow by undertow-io.
the class AbstractParserGenerator method writeStateMachine.
private void writeStateMachine(final String className, final ClassFile file, final CodeAttribute c, final State initial, final List<State> allStates, int noStates, final CustomStateMachine stateMachine, final ClassMethod sctor) {
//initial hasRemaining check
c.aload(BYTE_BUFFER_VAR);
c.invokevirtual(ByteBuffer.class.getName(), "hasRemaining", "()Z");
final BranchEnd nonZero = c.ifne();
//we have run out of bytes, return 0
c.iconst(0);
c.returnInstruction();
c.branchEnd(nonZero);
final List<State> states = new ArrayList<State>();
states.add(initial);
states.addAll(allStates);
Collections.sort(states);
//store the current state in a local variable
c.aload(PARSE_STATE_VAR);
c.dup();
c.getfield(parseStateClass, "stringBuilder", DescriptorUtils.makeDescriptor(StringBuilder.class));
c.astore(STATE_STRING_BUILDER_VAR);
c.dup();
c.getfield(parseStateClass, "parseState", "I");
c.dup();
c.istore(CURRENT_STATE_VAR);
//if this is state 0 there is a lot of stuff can ignore
BranchEnd optimizationEnd = c.ifeq();
c.dup();
c.getfield(parseStateClass, "pos", "I");
c.istore(STATE_POS_VAR);
c.dup();
c.getfield(parseStateClass, "current", HTTP_STRING_DESCRIPTOR);
c.astore(STATE_CURRENT_VAR);
c.getfield(parseStateClass, "currentBytes", "[B");
c.astore(STATE_CURRENT_BYTES_VAR);
//load the current state
c.iload(CURRENT_STATE_VAR);
//switch on the current state
TableSwitchBuilder builder = new TableSwitchBuilder(-2, noStates);
final IdentityHashMap<State, AtomicReference<BranchEnd>> ends = new IdentityHashMap<State, AtomicReference<BranchEnd>>();
final AtomicReference<BranchEnd> prefixMatch = builder.add();
final AtomicReference<BranchEnd> noState = builder.add();
ends.put(initial, builder.add());
for (final State s : states) {
if (s.stateno > 0) {
ends.put(s, builder.add());
}
}
c.tableswitch(builder);
stateNotFound(c, builder);
//return code
//code that synchronizes the state object and returns
setupLocalVariables(c);
final CodeLocation returnIncompleteCode = c.mark();
c.aload(PARSE_STATE_VAR);
c.dup();
c.dup();
c.dup();
c.dup();
c.iload(STATE_POS_VAR);
c.putfield(parseStateClass, "pos", "I");
c.aload(STATE_CURRENT_VAR);
c.putfield(parseStateClass, "current", HTTP_STRING_DESCRIPTOR);
c.aload(STATE_CURRENT_BYTES_VAR);
c.putfield(parseStateClass, "currentBytes", "[B");
c.iload(CURRENT_STATE_VAR);
c.putfield(parseStateClass, "parseState", "I");
c.returnInstruction();
setupLocalVariables(c);
final CodeLocation returnCompleteCode = c.mark();
c.aload(PARSE_STATE_VAR);
c.dup();
c.dup();
c.dup();
c.dup();
c.iconst(0);
c.putfield(parseStateClass, "pos", "I");
c.aconstNull();
c.putfield(parseStateClass, "current", HTTP_STRING_DESCRIPTOR);
c.aconstNull();
c.putfield(parseStateClass, "currentBytes", "[B");
c.aload(STATE_STRING_BUILDER_VAR);
c.iconst(0);
c.invokevirtual(StringBuilder.class.getName(), "setLength", "(I)V");
c.iconst(0);
c.putfield(parseStateClass, "parseState", "I");
c.returnInstruction();
//prefix
c.branchEnd(prefixMatch.get());
//loop for when we are prefix matching
final CodeLocation prefixLoop = c.mark();
handleReturnIfNoMoreBytes(c, returnIncompleteCode);
//load 3 copies of the current byte into the stack
c.aload(BYTE_BUFFER_VAR);
c.invokevirtual(ByteBuffer.class.getName(), "get", "()B");
c.dup();
c.dup();
final Set<BranchEnd> prefixHandleSpace = new HashSet<BranchEnd>();
if (stateMachine.isHeader()) {
c.iconst(':');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
}
c.iconst(' ');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\t');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\r');
prefixHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\n');
prefixHandleSpace.add(c.ifIcmpeq());
//check if we have overrun
c.aload(STATE_CURRENT_BYTES_VAR);
c.arraylength();
c.iload(STATE_POS_VAR);
BranchEnd overrun = c.ifIcmpeq();
//so we have not overrun
//now check if the character matches
c.dup();
c.aload(STATE_CURRENT_BYTES_VAR);
c.iload(STATE_POS_VAR);
c.baload();
c.isub();
BranchEnd noMatch = c.ifne();
//so they match
//pop our extra bytes off the stack, we do not need it
c.pop2();
c.iinc(STATE_POS_VAR, 1);
handleReturnIfNoMoreBytes(c, returnIncompleteCode);
c.gotoInstruction(prefixLoop);
//overrun and not match use the same code path
c.branchEnd(overrun);
//the current character did not match
c.branchEnd(noMatch);
c.iconst(NO_STATE);
c.istore(CURRENT_STATE_VAR);
//create the string builder
c.aload(STATE_STRING_BUILDER_VAR);
c.aload(STATE_CURRENT_VAR);
c.invokevirtual(HTTP_STRING_CLASS, "toString", "()Ljava/lang/String;");
c.iconst(0);
c.iload(STATE_POS_VAR);
c.invokevirtual(String.class.getName(), "substring", "(II)Ljava/lang/String;");
c.invokevirtual(StringBuilder.class.getName(), "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
c.swap();
c.invokevirtual(StringBuilder.class.getName(), "append", "(C)Ljava/lang/StringBuilder;");
c.pop2();
BranchEnd prefixToNoState = c.gotoInstruction();
//handle the space case
for (BranchEnd b : prefixHandleSpace) {
c.branchEnd(b);
}
//new state will be 0
c.iconst(0);
c.istore(CURRENT_STATE_VAR);
c.aload(STATE_CURRENT_BYTES_VAR);
c.arraylength();
c.iload(STATE_POS_VAR);
BranchEnd correctLength = c.ifIcmpeq();
c.newInstruction(HTTP_STRING_CLASS);
c.dup();
c.aload(STATE_CURRENT_BYTES_VAR);
c.iconst(0);
c.iload(STATE_POS_VAR);
c.invokespecial(HTTP_STRING_CLASS, "<init>", "([BII)V");
stateMachine.handleOtherToken(c);
//TODO: exit if it returns null
//decrease the available bytes
c.pop();
tokenDone(c, returnCompleteCode, stateMachine);
c.branchEnd(correctLength);
c.aload(STATE_CURRENT_VAR);
stateMachine.handleStateMachineMatchedToken(c);
//TODO: exit if it returns null
c.pop();
tokenDone(c, returnCompleteCode, stateMachine);
//nostate
c.branchEnd(noState.get());
c.branchEnd(prefixToNoState);
CodeLocation noStateLoop = c.mark();
handleReturnIfNoMoreBytes(c, returnIncompleteCode);
//load 2 copies of the current byte into the stack
c.aload(BYTE_BUFFER_VAR);
c.invokevirtual(ByteBuffer.class.getName(), "get", "()B");
c.dup();
final Set<BranchEnd> nostateHandleSpace = new HashSet<BranchEnd>();
if (stateMachine.isHeader()) {
c.iconst(':');
nostateHandleSpace.add(c.ifIcmpeq());
c.dup();
}
c.iconst(' ');
nostateHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\t');
nostateHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\r');
nostateHandleSpace.add(c.ifIcmpeq());
c.dup();
c.iconst('\n');
nostateHandleSpace.add(c.ifIcmpeq());
c.aload(STATE_STRING_BUILDER_VAR);
c.swap();
c.invokevirtual(StringBuilder.class.getName(), "append", "(C)Ljava/lang/StringBuilder;");
c.pop();
c.aload(BYTE_BUFFER_VAR);
c.invokevirtual(ByteBuffer.class.getName(), "hasRemaining", "()Z");
//go back to the start if we have not run out of bytes
c.ifne(noStateLoop);
//we have run out of bytes, so we need to write back the current state
c.aload(PARSE_STATE_VAR);
c.iload(CURRENT_STATE_VAR);
c.putfield(parseStateClass, "parseState", "I");
c.iconst(0);
c.returnInstruction();
for (BranchEnd b : nostateHandleSpace) {
c.branchEnd(b);
}
c.aload(STATE_STRING_BUILDER_VAR);
c.invokevirtual(StringBuilder.class.getName(), "toString", "()Ljava/lang/String;");
c.newInstruction(HTTP_STRING_CLASS);
c.dupX1();
c.swap();
c.invokespecial(HTTP_STRING_CLASS, "<init>", "(Ljava/lang/String;)V");
stateMachine.handleOtherToken(c);
//TODO: exit if it returns null
tokenDone(c, returnCompleteCode, stateMachine);
c.branchEnd(optimizationEnd);
c.pop();
c.iconst(0);
c.istore(STATE_POS_VAR);
c.aconstNull();
c.astore(STATE_CURRENT_VAR);
c.aconstNull();
c.astore(STATE_CURRENT_BYTES_VAR);
c.branchEnd(ends.get(initial).get());
invokeState(className, file, c, initial, initial, noStateLoop, prefixLoop, returnIncompleteCode, returnCompleteCode, stateMachine);
for (final State s : allStates) {
if (s.stateno >= 0) {
c.branchEnd(ends.get(s).get());
invokeState(className, file, c, s, initial, noStateLoop, prefixLoop, returnIncompleteCode, returnCompleteCode, stateMachine);
}
}
}
Aggregations