From 825acfc85ac7070e40e8a50f53156b0b6ca35214 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 3 Dec 2016 19:45:59 +0300 Subject: [PATCH] Add IR parser of invoke instructions --- .../model/instructions/InvokeInstruction.java | 4 - .../model/text/InstructionStringifier.java | 85 ++++++++++++++----- .../org/teavm/model/text/ListingLexer.java | 19 +++-- .../org/teavm/model/text/ListingParser.java | 67 +++++++++++++++ .../java/org/teavm/model/text/ParserTest.java | 30 +++++++ .../test/resources/model/text/invocation.txt | 5 ++ 6 files changed, 176 insertions(+), 34 deletions(-) create mode 100644 core/src/test/resources/model/text/invocation.txt diff --git a/core/src/main/java/org/teavm/model/instructions/InvokeInstruction.java b/core/src/main/java/org/teavm/model/instructions/InvokeInstruction.java index 8bf04206e..c9cadea23 100644 --- a/core/src/main/java/org/teavm/model/instructions/InvokeInstruction.java +++ b/core/src/main/java/org/teavm/model/instructions/InvokeInstruction.java @@ -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; diff --git a/core/src/main/java/org/teavm/model/text/InstructionStringifier.java b/core/src/main/java/org/teavm/model/text/InstructionStringifier.java index fac364046..b54afacac 100644 --- a/core/src/main/java/org/teavm/model/text/InstructionStringifier.java +++ b/core/src/main/java/org/teavm/model/text/InstructionStringifier.java @@ -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 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 diff --git a/core/src/main/java/org/teavm/model/text/ListingLexer.java b/core/src/main/java/org/teavm/model/text/ListingLexer.java index 147bbc0ff..d8ffdc43d 100644 --- a/core/src/main/java/org/teavm/model/text/ListingLexer.java +++ b/core/src/main/java/org/teavm/model/text/ListingLexer.java @@ -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); } diff --git a/core/src/main/java/org/teavm/model/text/ListingParser.java b/core/src/main/java/org/teavm/model/text/ListingParser.java index a96db4fbd..e1d5b9b31 100644 --- a/core/src/main/java/org/teavm/model/text/ListingParser.java +++ b/core/src/main/java/org/teavm/model/text/ListingParser.java @@ -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 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(); diff --git a/core/src/test/java/org/teavm/model/text/ParserTest.java b/core/src/test/java/org/teavm/model/text/ParserTest.java index b101f3e4e..0be254cba 100644 --- a/core/src/test/java/org/teavm/model/text/ParserTest.java +++ b/core/src/test/java/org/teavm/model/text/ParserTest.java @@ -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"; diff --git a/core/src/test/resources/model/text/invocation.txt b/core/src/test/resources/model/text/invocation.txt new file mode 100644 index 000000000..001c716fc --- /dev/null +++ b/core/src/test/resources/model/text/invocation.txt @@ -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 \ No newline at end of file