mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
Add IR parser of invoke instructions
This commit is contained in:
parent
c16de76b23
commit
825acfc85a
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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";
|
||||
|
5
core/src/test/resources/model/text/invocation.txt
Normal file
5
core/src/test/resources/model/text/invocation.txt
Normal 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
|
Loading…
Reference in New Issue
Block a user