diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index b96ae6e54..d17214909 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -440,5 +440,18 @@ class DependencyGraphBuilder { } }); } + + @Override + public void nullCheck(VariableReader receiver, VariableReader value) { + DependencyNode valueNode = nodes[value.getIndex()]; + DependencyNode receiverNode = nodes[receiver.getIndex()]; + valueNode.connect(receiverNode); + useRunners.add(new Runnable() { + @Override public void run() { + dependencyChecker.linkMethod(new MethodReference("java.lang.NullPointerException", + "", ValueType.VOID), callerStack); + } + }); + } }; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java b/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java index 25937fba0..f915886d9 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java @@ -18,6 +18,7 @@ package org.teavm.javascript; import org.teavm.model.*; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.NullCheckInstruction; /** * @@ -46,12 +47,12 @@ public class NullPointerExceptionTransformer implements ClassHolderTransformer { if (invoke.getType() != InvocationType.VIRTUAL) { continue; } - InvokeInstruction checkInvoke = new InvokeInstruction(); - checkInvoke.setMethod(new MethodReference(RuntimeSupport.class.getName(), "requireNonNull", - ValueType.object("java.lang.Object"), ValueType.VOID)); - checkInvoke.setType(InvocationType.SPECIAL); - checkInvoke.getArguments().add(invoke.getInstance()); - block.getInstructions().add(i++, checkInvoke); + NullCheckInstruction nullCheck = new NullCheckInstruction(); + nullCheck.setValue(invoke.getInstance()); + Variable var = block.getProgram().createVariable(); + nullCheck.setReceiver(var); + invoke.setInstance(var); + block.getInstructions().add(i++, nullCheck); } } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index a9da00e6c..e896ebbf2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -79,6 +79,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { renderRuntimeString(); renderRuntimeUnwrapString(); renderRuntimeObjcls(); + renderRuntimeNullCheck(); } catch (NamingException e) { throw new RenderingException("Error rendering runtime methods. See a cause for details", e); } catch (IOException e) { @@ -93,8 +94,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.append("var cls").ws().append("=").ws().append("clsProto.classObject;").softNewLine(); writer.append("if").ws().append("(cls").ws().append("===").ws().append("undefined)").ws() .append("{").softNewLine().indent(); - MethodReference createMethodRef = new MethodReference(classClass, new MethodDescriptor("createNew", - ValueType.object(classClass))); + MethodReference createMethodRef = new MethodReference(classClass, "createNew", ValueType.object(classClass)); writer.append("cls").ws().append("=").ws().appendMethodBody(createMethodRef).append("();").softNewLine(); writer.append("cls.$data = clsProto;").softNewLine(); if (classSource.get(classClass).getField("name") != null) { @@ -124,8 +124,8 @@ public class Renderer implements ExprVisitor, StatementVisitor { private void renderRuntimeString() throws IOException { String stringClass = "java.lang.String"; - MethodReference stringCons = new MethodReference(stringClass, new MethodDescriptor("", - ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID)); + MethodReference stringCons = new MethodReference(stringClass, "", + ValueType.arrayOf(ValueType.CHARACTER), ValueType.VOID); writer.append("$rt_str = function(str) {").indent().softNewLine(); writer.append("var characters = $rt_createCharArray(str.length);").softNewLine(); writer.append("var charsBuffer = characters.data;").softNewLine(); @@ -139,16 +139,14 @@ public class Renderer implements ExprVisitor, StatementVisitor { private void renderRuntimeUnwrapString() throws IOException { String stringClass = "java.lang.String"; - MethodReference stringLen = new MethodReference(stringClass, new MethodDescriptor( - "length", ValueType.INTEGER)); - MethodReference getChars = new MethodReference(stringClass, new MethodDescriptor( - "getChars", ValueType.INTEGER, ValueType.INTEGER, ValueType.arrayOf(ValueType.CHARACTER), - ValueType.INTEGER, ValueType.VOID)); + MethodReference stringLen = new MethodReference(stringClass, "length", ValueType.INTEGER); + MethodReference getChars = new MethodReference(stringClass, "getChars", ValueType.INTEGER, ValueType.INTEGER, + ValueType.arrayOf(ValueType.CHARACTER), ValueType.INTEGER, ValueType.VOID); writer.append("$rt_ustr = function(str) {").indent().softNewLine(); writer.append("var result = \"\";").softNewLine(); writer.append("var sz = ").appendMethodBody(stringLen).append("(str);").softNewLine(); - writer.append("var array = $rt_createCharArray(sz);"); - writer.appendMethodBody(getChars).append("(str, 0, sz, array, 0);"); + writer.append("var array = $rt_createCharArray(sz);").softNewLine(); + writer.appendMethodBody(getChars).append("(str, 0, sz, array, 0);").softNewLine(); writer.append("for (var i = 0; i < sz; i = (i + 1) | 0) {").indent().softNewLine(); writer.append("result += String.fromCharCode(array.data[i]);").softNewLine(); writer.outdent().append("}").softNewLine(); @@ -156,6 +154,17 @@ public class Renderer implements ExprVisitor, StatementVisitor { writer.outdent().append("}").newLine(); } + private void renderRuntimeNullCheck() throws IOException { + String npe = "java.lang.NullPointerException"; + writer.append("$rt_nullCheck = function(val) {").indent().softNewLine(); + writer.append("if (val === null) {").indent().softNewLine(); + writer.append("$rt_throw(").appendClass(npe).append('.').appendMethod(npe, "", ValueType.VOID) + .append("());").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return val;").softNewLine(); + writer.outdent().append("}").newLine(); + } + private void renderRuntimeObjcls() throws IOException { writer.append("$rt_objcls = function() { return ").appendClass("java.lang.Object").append("; }").newLine(); } @@ -217,7 +226,7 @@ public class Renderer implements ExprVisitor, StatementVisitor { .append("function()").ws().append("{").ws() .appendClass(cls.getName()).append("_$clinit();").ws().append("}"); } - writer.ws().append("});").softNewLine().outdent(); + writer.ws().append("});").newLine().outdent(); List nonInitMethods = new ArrayList<>(); if (!cls.getModifiers().contains(NodeModifier.INTERFACE)) { writer.append("function ").appendClass(cls.getName()).append("_$clinit()").ws() @@ -860,6 +869,11 @@ public class Renderer implements ExprVisitor, StatementVisitor { expr.getOperand().acceptVisitor(this); writer.append(')'); break; + case NULL_CHECK: + writer.append("$rt_nullCheck("); + expr.getOperand().acceptVisitor(this); + writer.append(')'); + break; } } catch (IOException e) { throw new RenderingException("IO error occured", e); diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index f0b98e317..f7f124cdd 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -613,4 +613,10 @@ class StatementGenerator implements InstructionVisitor { public void visit(InitClassInstruction insn) { statements.add(Statement.initClass(insn.getClassName())); } + + @Override + public void visit(NullCheckInstruction insn) { + assign(Expr.unary(UnaryOperation.NULL_CHECK, Expr.var(insn.getValue().getIndex())), + insn.getReceiver().getIndex()); + } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/UnaryOperation.java b/teavm-core/src/main/java/org/teavm/javascript/ast/UnaryOperation.java index fa8e7b74d..0fa0d41bb 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/UnaryOperation.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/UnaryOperation.java @@ -29,5 +29,6 @@ public enum UnaryOperation { NUM_TO_LONG, INT_TO_LONG, BYTE_TO_INT, - SHORT_TO_INT + SHORT_TO_INT, + NULL_CHECK } diff --git a/teavm-core/src/main/java/org/teavm/model/InstructionReadVisitor.java b/teavm-core/src/main/java/org/teavm/model/InstructionReadVisitor.java index 92dd04378..310a7f1b7 100644 --- a/teavm-core/src/main/java/org/teavm/model/InstructionReadVisitor.java +++ b/teavm-core/src/main/java/org/teavm/model/InstructionReadVisitor.java @@ -196,4 +196,9 @@ class InstructionReadVisitor implements InstructionVisitor { public void visit(InitClassInstruction insn) { reader.initClass(insn.getClassName()); } + + @Override + public void visit(NullCheckInstruction insn) { + reader.nullCheck(insn.getReceiver(), insn.getValue()); + } } diff --git a/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java b/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java index 5df676fed..05b67da70 100644 --- a/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java +++ b/teavm-core/src/main/java/org/teavm/model/instructions/InstructionReader.java @@ -96,4 +96,6 @@ public interface InstructionReader { void isInstance(VariableReader receiver, VariableReader value, ValueType type); void initClass(String className); + + void nullCheck(VariableReader receiver, VariableReader value); } diff --git a/teavm-core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java b/teavm-core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java index 243139214..865335d5d 100644 --- a/teavm-core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java +++ b/teavm-core/src/main/java/org/teavm/model/instructions/InstructionVisitor.java @@ -85,4 +85,6 @@ public interface InstructionVisitor { void visit(IsInstanceInstruction insn); void visit(InitClassInstruction insn); + + void visit(NullCheckInstruction insn); } diff --git a/teavm-core/src/main/java/org/teavm/model/instructions/NullCheckInstruction.java b/teavm-core/src/main/java/org/teavm/model/instructions/NullCheckInstruction.java new file mode 100644 index 000000000..1a48c5d34 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/instructions/NullCheckInstruction.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model.instructions; + +import org.teavm.model.Instruction; +import org.teavm.model.Variable; + +/** + * + * @author Alexey Andreev + */ +public class NullCheckInstruction extends Instruction { + private Variable value; + private Variable receiver; + + public Variable getValue() { + return value; + } + + public void setValue(Variable value) { + this.value = value; + } + + public Variable getReceiver() { + return receiver; + } + + public void setReceiver(Variable receiver) { + this.receiver = receiver; + } + + @Override + public void acceptVisitor(InstructionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/util/BasicBlockMapper.java b/teavm-core/src/main/java/org/teavm/model/util/BasicBlockMapper.java index 3c99cd1af..520b90689 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/BasicBlockMapper.java +++ b/teavm-core/src/main/java/org/teavm/model/util/BasicBlockMapper.java @@ -180,4 +180,8 @@ public abstract class BasicBlockMapper implements InstructionVisitor { @Override public void visit(InitClassInstruction insn) { } + + @Override + public void visit(NullCheckInstruction insn) { + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java b/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java index 16ea86fc9..7d409f059 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java +++ b/teavm-core/src/main/java/org/teavm/model/util/DefinitionExtractor.java @@ -197,4 +197,9 @@ public class DefinitionExtractor implements InstructionVisitor { public void visit(InitClassInstruction insn) { definedVariables = new Variable[0]; } + + @Override + public void visit(NullCheckInstruction insn) { + definedVariables = new Variable[] { insn.getReceiver() }; + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java index 8da4e3e2a..d69189773 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java +++ b/teavm-core/src/main/java/org/teavm/model/util/InstructionStringifier.java @@ -337,4 +337,9 @@ public class InstructionStringifier implements InstructionReader { public void initClass(String className) { sb.append("initclass ").append(className); } + + @Override + public void nullCheck(VariableReader receiver, VariableReader value) { + sb.append("@").append(receiver.getIndex()).append(" := nullCheck @").append(value.getIndex()); + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java b/teavm-core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java index ff671d846..366134a22 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java +++ b/teavm-core/src/main/java/org/teavm/model/util/InstructionTransitionExtractor.java @@ -199,4 +199,9 @@ public class InstructionTransitionExtractor implements InstructionVisitor { public void visit(InitClassInstruction insn) { targets = null; } + + @Override + public void visit(NullCheckInstruction insn) { + targets = null; + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/InstructionVariableMapper.java b/teavm-core/src/main/java/org/teavm/model/util/InstructionVariableMapper.java index 49eb8ca05..2e6ad423d 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/InstructionVariableMapper.java +++ b/teavm-core/src/main/java/org/teavm/model/util/InstructionVariableMapper.java @@ -223,4 +223,9 @@ public abstract class InstructionVariableMapper implements InstructionVisitor { public void visit(InitClassInstruction insn) { } + @Override + public void visit(NullCheckInstruction insn) { + insn.setReceiver(map(insn.getReceiver())); + insn.setValue(map(insn.getValue())); + } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java index 8eff354be..4110c2f60 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -396,5 +396,13 @@ public class ProgramUtils { insnCopy.setClassName(insn.getClassName()); copy = insnCopy; } + + @Override + public void visit(NullCheckInstruction insn) { + NullCheckInstruction insnCopy = new NullCheckInstruction(); + insnCopy.setReceiver(copyVar(insn.getReceiver())); + insnCopy.setValue(copyVar(insn.getValue())); + copy = insnCopy; + } } } diff --git a/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java b/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java index e2f842371..803ab0fd5 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java +++ b/teavm-core/src/main/java/org/teavm/model/util/UsageExtractor.java @@ -201,4 +201,9 @@ public class UsageExtractor implements InstructionVisitor { public void visit(InitClassInstruction insn) { usedVariables = new Variable[0]; } + + @Override + public void visit(NullCheckInstruction insn) { + usedVariables = new Variable[] { insn.getValue() }; + } } diff --git a/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java b/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java index be916f7a8..22535d1bd 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java +++ b/teavm-core/src/main/java/org/teavm/optimization/CommonSubexpressionElimination.java @@ -406,5 +406,12 @@ public class CommonSubexpressionElimination implements MethodOptimization { @Override public void visit(InitClassInstruction insn) { } + + @Override + public void visit(NullCheckInstruction insn) { + int val = map[insn.getValue().getIndex()]; + insn.setValue(program.variableAt(val)); + bind(insn.getReceiver().getIndex(), "nullCheck @" + val); + } }; } diff --git a/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java b/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java index f0b49e3cc..657de1456 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java +++ b/teavm-core/src/main/java/org/teavm/optimization/UnusedVariableElimination.java @@ -243,5 +243,10 @@ public class UnusedVariableElimination implements MethodOptimization { @Override public void visit(InitClassInstruction insn) { } + + @Override + public void visit(NullCheckInstruction insn) { + requestUsage(insn.getReceiver()); + } } } diff --git a/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java b/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java index 4f0e3d1c7..5e9b1b0a0 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java +++ b/teavm-core/src/main/java/org/teavm/optimization/VariableEscapeAnalyzer.java @@ -197,5 +197,9 @@ public class VariableEscapeAnalyzer { @Override public void visit(InitClassInstruction insn) { } + + @Override + public void visit(NullCheckInstruction insn) { + } } } diff --git a/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java b/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java index 1e92e63c7..5047b2951 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/optimization/VariableUsageGraphBuilder.java @@ -202,5 +202,10 @@ public class VariableUsageGraphBuilder { @Override public void visit(InitClassInstruction insn) { } + + @Override + public void visit(NullCheckInstruction insn) { + use(insn.getReceiver(), insn.getValue()); + } } } diff --git a/teavm-core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java b/teavm-core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java index 8256f764d..8ba12f15a 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ClassRefsRenamer.java @@ -277,4 +277,8 @@ class ClassRefsRenamer implements InstructionVisitor { public void visit(InitClassInstruction insn) { insn.setClassName(classNameMapper.map(insn.getClassName())); } + + @Override + public void visit(NullCheckInstruction insn) { + } } diff --git a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java index b13f6cc72..fd8d2585f 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java +++ b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java @@ -424,5 +424,11 @@ public class SSATransformer { @Override public void visit(InitClassInstruction insn) { } + + @Override + public void visit(NullCheckInstruction insn) { + insn.setValue(use(insn.getValue())); + insn.setReceiver(define(insn.getReceiver())); + } }; }