Add IR parser of invoke instructions

This commit is contained in:
Alexey Andreev 2016-12-03 19:45:59 +03:00
parent c16de76b23
commit 825acfc85a
6 changed files with 176 additions and 34 deletions

View File

@ -21,10 +21,6 @@ import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Variable;
/**
*
* @author Alexey Andreev
*/
public class InvokeInstruction extends Instruction {
private InvocationType type;
private MethodReference method;

View File

@ -17,6 +17,7 @@ package org.teavm.model.text;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
@ -45,7 +46,8 @@ class InstructionStringifier implements InstructionReader {
@Override
public void classConstant(VariableReader receiver, ValueType cst) {
sb.append("@").append(receiver.getIndex()).append(" := classOf ").append(cst);
sb.append("@").append(receiver.getIndex()).append(" := classOf ");
escapeIdentifierIfNeeded(cst.toString(), sb);
}
@Override
@ -112,6 +114,27 @@ class InstructionStringifier implements InstructionReader {
}
}
private static void escapeIdentifierIfNeeded(String s, StringBuilder sb) {
boolean needsEscaping = false;
if (s.isEmpty()) {
needsEscaping = true;
} else if (!ListingLexer.isIdentifierStart(s.charAt(0))) {
needsEscaping = true;
} else {
for (int i = 1; i < s.length(); ++i) {
if (!ListingLexer.isIdentifierPart(s.charAt(i))) {
needsEscaping = true;
break;
}
}
}
if (needsEscaping) {
sb.append('`').append(s).append('`');
} else {
sb.append(s);
}
}
@Override
public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
NumericOperandType type) {
@ -170,7 +193,8 @@ class InstructionStringifier implements InstructionReader {
@Override
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
sb.append("@").append(receiver.getIndex()).append(" := cast @").append(value.getIndex())
.append(" to ").append(targetType);
.append(" to ");
escapeIdentifierIfNeeded(targetType.toString(), sb);
}
@Override
@ -186,10 +210,10 @@ class InstructionStringifier implements InstructionReader {
sb.append("@").append(receiver.getIndex()).append(" := cast @").append(value.getIndex());
switch (direction) {
case FROM_INTEGER:
sb.append(" from INT to ").append(type);
sb.append(" from int to ").append(type.name().toLowerCase(Locale.ROOT));
break;
case TO_INTEGER:
sb.append(" from ").append(type).append(" to INT");
sb.append(" from ").append(type.name().toLowerCase(Locale.ROOT)).append(" to int");
break;
}
}
@ -263,9 +287,9 @@ class InstructionStringifier implements InstructionReader {
sb.append("; ");
}
SwitchTableEntryReader entry = table.get(i);
sb.append("case ").append(entry.getCondition()).append(": goto $").append(entry.getTarget().getIndex());
sb.append("if ").append(entry.getCondition()).append(" goto $").append(entry.getTarget().getIndex());
}
sb.append(", default: goto $").append(defaultTarget.getIndex());
sb.append(" else goto $").append(defaultTarget.getIndex());
}
@Override
@ -283,13 +307,17 @@ class InstructionStringifier implements InstructionReader {
@Override
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
sb.append("@").append(receiver.getIndex()).append(" := new ").append(itemType).append("[@")
.append(size.getIndex()).append(']');
sb.append("@").append(receiver.getIndex()).append(" := new ");
escapeIdentifierIfNeeded(itemType.toString(), sb);
sb.append("[@").append(size.getIndex()).append(']');
}
@Override
public void createArray(VariableReader receiver, ValueType itemType, List<? extends VariableReader> dimensions) {
sb.append("@").append(receiver.getIndex()).append(" := new ").append(itemType).append("[");
sb.append("@").append(receiver.getIndex()).append(" := new ");
escapeIdentifierIfNeeded(itemType.toString(), sb);
sb.append("[");
for (int i = 0; i < dimensions.size(); ++i) {
if (i > 0) {
sb.append(", ");
@ -301,12 +329,15 @@ class InstructionStringifier implements InstructionReader {
@Override
public void create(VariableReader receiver, String type) {
sb.append("@").append(receiver.getIndex()).append(" := new ").append(type).append("");
sb.append("@").append(receiver.getIndex()).append(" := new ");
escapeIdentifierIfNeeded(type, sb);
sb.append("");
}
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field, ValueType fieldType) {
sb.append("@").append(receiver.getIndex()).append(" := field " + field);
sb.append("@").append(receiver.getIndex()).append(" := field ");
escapeIdentifierIfNeeded(field.toString(), sb);
sb.append(field);
if (instance != null) {
sb.append(" @").append(instance.getIndex());
@ -315,7 +346,8 @@ class InstructionStringifier implements InstructionReader {
@Override
public void putField(VariableReader instance, FieldReference field, VariableReader value, ValueType fieldType) {
sb.append("field " + field);
sb.append("field ");
escapeIdentifierIfNeeded(field.toString(), sb);
if (instance != null) {
sb.append(" @").append(instance.getIndex());
}
@ -329,19 +361,20 @@ class InstructionStringifier implements InstructionReader {
@Override
public void cloneArray(VariableReader receiver, VariableReader array) {
sb.append("@").append(receiver.getIndex()).append(" := clone @").append(array.getIndex()).append("");
sb.append("@").append(receiver.getIndex()).append(" := clone @").append(array.getIndex());
}
@Override
public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
sb.append("@").append(receiver.getIndex()).append(" := data @").append(array.getIndex()).append("");
sb.append("@").append(receiver.getIndex()).append(" := data @").append(array.getIndex()).append(" as ")
.append(elementType.name().toLowerCase(Locale.ROOT));
}
@Override
public void getElement(VariableReader receiver, VariableReader array, VariableReader index,
ArrayElementType type) {
sb.append("@").append(receiver.getIndex()).append(" := @").append(array.getIndex()).append("[@")
.append(index.getIndex()).append("]").append(" as " + type.name().toLowerCase());
.append(index.getIndex()).append("]").append(" as " + type.name().toLowerCase(Locale.ROOT));
}
@Override
@ -356,15 +389,20 @@ class InstructionStringifier implements InstructionReader {
if (receiver != null) {
sb.append("@").append(receiver.getIndex()).append(" := ");
}
switch (type) {
case SPECIAL:
sb.append("invoke ");
break;
case VIRTUAL:
sb.append("invokeVirtual ");
break;
if (instance == null) {
sb.append("invokeStatic ");
} else {
switch (type) {
case SPECIAL:
sb.append("invoke ");
break;
case VIRTUAL:
sb.append("invokeVirtual ");
break;
}
}
escapeIdentifierIfNeeded(method.toString(), sb);
if (instance != null) {
sb.append("@").append(instance.getIndex());
}
@ -447,7 +485,8 @@ class InstructionStringifier implements InstructionReader {
@Override
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
sb.append("@").append(receiver.getIndex()).append(" := @").append(value.getIndex())
.append(" instanceOf ").append(type);
.append(" instanceOf ");
escapeIdentifierIfNeeded(type.toString(), sb);
}
@Override

View File

@ -187,7 +187,7 @@ class ListingLexer {
readEscapedIdentifier();
break;
default:
if (isIdentifierStart()) {
if (isIdentifierStart(c)) {
readIdentifier();
} else if (c >= '0' && c <= '9') {
readNumber();
@ -212,7 +212,7 @@ class ListingLexer {
private void readIdentifierLike() throws IOException {
StringBuilder sb = new StringBuilder();
while (isIdentifierPart()) {
while (isIdentifierPart(c)) {
sb.append((char) c);
nextChar();
}
@ -224,7 +224,7 @@ class ListingLexer {
StringBuilder sb = new StringBuilder();
sb.append((char) c);
nextChar();
while (isIdentifierPart()) {
while (isIdentifierPart(c)) {
sb.append((char) c);
nextChar();
}
@ -244,7 +244,7 @@ class ListingLexer {
tokenValue = sb.toString();
}
private boolean isIdentifierStart() {
static boolean isIdentifierStart(int c) {
if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
return true;
}
@ -256,8 +256,8 @@ class ListingLexer {
}
}
private boolean isIdentifierPart() {
if (isIdentifierStart() || c >= '0' && c <= '9') {
static boolean isIdentifierPart(int c) {
if (isIdentifierStart(c) || c >= '0' && c <= '9') {
return true;
}
switch (c) {
@ -330,6 +330,11 @@ class ListingLexer {
token = ListingToken.INTEGER;
if (c == '-') {
sb.append(c);
nextChar();
}
while (c >= '0' && c <= '9') {
sb.append((char) c);
nextChar();
@ -374,7 +379,7 @@ class ListingLexer {
} else if (c == 'l' || c == 'L') {
nextChar();
token = ListingToken.LONG;
} else if (isIdentifierStart()) {
} else if (isIdentifierStart(c)) {
throw new ListingParseException("Wrong number", index);
}

View File

@ -17,13 +17,16 @@ package org.teavm.model.text;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.teavm.model.BasicBlock;
import org.teavm.model.Incoming;
import org.teavm.model.MethodReference;
import org.teavm.model.Phi;
import org.teavm.model.Program;
import org.teavm.model.TextLocation;
@ -43,6 +46,8 @@ import org.teavm.model.instructions.EmptyInstruction;
import org.teavm.model.instructions.ExitInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.NumericOperandType;
@ -194,6 +199,12 @@ public class ListingParser {
parseIf(block);
break;
}
case "invoke":
case "invokeStatic":
case "invokeVirtual": {
parseInvoke(block, null);
break;
}
default:
unexpected();
break;
@ -274,6 +285,11 @@ public class ListingParser {
lexer.nextToken();
parseClassLiteral(block, receiver);
break;
case "invoke":
case "invokeVirtual":
case "invokeStatic":
parseInvoke(block, receiver);
break;
default:
unexpected();
break;
@ -485,6 +501,57 @@ public class ListingParser {
lexer.nextToken();
}
private void parseInvoke(BasicBlock block, Variable receiver) throws IOException, ListingParseException {
InvokeInstruction insn = new InvokeInstruction();
insn.setReceiver(receiver);
boolean hasInstance = true;
switch ((String) lexer.getTokenValue()) {
case "invoke":
insn.setType(InvocationType.SPECIAL);
break;
case "invokeStatic":
insn.setType(InvocationType.SPECIAL);
hasInstance = false;
break;
case "invokeVirtual":
insn.setType(InvocationType.VIRTUAL);
break;
}
lexer.nextToken();
expect(ListingToken.IDENTIFIER);
MethodReference method = MethodReference.parseIfPossible((String) lexer.getTokenValue());
if (method == null) {
throw new ListingParseException("Unparseable method", lexer.getIndex());
}
insn.setMethod(method);
lexer.nextToken();
List<Variable> arguments = new ArrayList<>();
if (lexer.getToken() == ListingToken.VARIABLE) {
arguments.add(expectVariable());
while (lexer.getToken() == ListingToken.COMMA) {
lexer.nextToken();
arguments.add(expectVariable());
}
}
if (hasInstance) {
if (arguments.isEmpty()) {
throw new ListingParseException("This kind of invocation requires at least one argument",
lexer.getIndex());
}
insn.setInstance(arguments.get(0));
insn.getArguments().addAll(arguments.subList(1, arguments.size()));
} else {
insn.getArguments().addAll(arguments);
}
block.getInstructions().add(insn);
}
private void parseIf(BasicBlock block) throws IOException, ListingParseException {
Variable first = expectVariable();

View File

@ -16,6 +16,8 @@
package org.teavm.model.text;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
@ -28,6 +30,8 @@ import org.teavm.model.instructions.ClassConstantInstruction;
import org.teavm.model.instructions.DoubleConstantInstruction;
import org.teavm.model.instructions.FloatConstantInstruction;
import org.teavm.model.instructions.IntegerConstantInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.LongConstantInstruction;
import org.teavm.model.instructions.StringConstantInstruction;
@ -72,6 +76,32 @@ public class ParserTest {
assertTrue("ClassConstant", block.getInstructions().get(5) instanceof ClassConstantInstruction);
}
@Test
public void invocation() throws Exception {
Program program = runTest("invocation");
assertEquals(1, program.basicBlockCount());
BasicBlock block = program.basicBlockAt(0);
assertTrue(block.getInstructions().get(0) instanceof InvokeInstruction);
assertTrue(block.getInstructions().get(1) instanceof InvokeInstruction);
assertTrue(block.getInstructions().get(2) instanceof InvokeInstruction);
InvokeInstruction invoke = (InvokeInstruction) block.getInstructions().get(0);
assertEquals(InvocationType.VIRTUAL, invoke.getType());
assertEquals(0, invoke.getArguments().size());
assertNotNull(invoke.getInstance());
invoke = (InvokeInstruction) block.getInstructions().get(1);
assertEquals(InvocationType.SPECIAL, invoke.getType());
assertEquals(1, invoke.getArguments().size());
assertNull(invoke.getInstance());
invoke = (InvokeInstruction) block.getInstructions().get(2);
assertEquals(InvocationType.SPECIAL, invoke.getType());
assertEquals(1, invoke.getArguments().size());
assertNotNull(invoke.getInstance());
}
private Program runTest(String name) throws IOException {
ClassLoader classLoader = ParserTest.class.getClassLoader();
String path = "model/text/" + name + ".txt";

View File

@ -0,0 +1,5 @@
$block
@a := invokeVirtual `java.lang.String.toString()Ljava/lang/String;` @o
@b := invokeStatic `java.lang.Integer.parseInt(Ljava/lang/String;)I` @a
invoke `java.lang.String.charAt(I)C` @a, @b
return @b