From 6f50eefaf9f082d8ead5764b6b5997843898ac87 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 4 Oct 2019 18:44:05 +0300 Subject: [PATCH] JS: add null check in strict mode --- .../classlib/java/lang/ClassGenerator.java | 4 +- .../java/org/teavm/backend/c/CTarget.java | 5 ++- .../backend/javascript/JavaScriptTarget.java | 9 ++++- .../javascript/rendering/Precedence.java | 1 + .../rendering/StatementRenderer.java | 15 ++++++-- .../lowlevel/LowLevelNullCheckFilter.java | 38 +++++++++++++++++++ .../BoundCheckInsertion.java | 4 +- .../model/transformation/NullCheckFilter.java | 37 ++++++++++++++++++ .../NullCheckInsertion.java | 19 +++++----- 9 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 core/src/main/java/org/teavm/model/lowlevel/LowLevelNullCheckFilter.java rename core/src/main/java/org/teavm/model/{lowlevel => transformation}/BoundCheckInsertion.java (99%) create mode 100644 core/src/main/java/org/teavm/model/transformation/NullCheckFilter.java rename core/src/main/java/org/teavm/model/{lowlevel => transformation}/NullCheckInsertion.java (90%) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java index 9d72ab303..306fe3bd1 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java @@ -55,10 +55,10 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin { public void generate(InjectorContext context, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "newEmptyInstance": - context.getWriter().append("new "); + context.getWriter().append("new ("); context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS); context.getWriter().append('.').appendField(platformClassField); - context.getWriter().append("()"); + context.getWriter().append(")"); break; } } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 8c742b30d..bb521b67d 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -109,10 +109,11 @@ import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ExportDependencyListener; -import org.teavm.model.lowlevel.NullCheckInsertion; +import org.teavm.model.lowlevel.LowLevelNullCheckFilter; import org.teavm.model.lowlevel.NullCheckTransformation; import org.teavm.model.lowlevel.ShadowStackTransformer; import org.teavm.model.transformation.ClassPatch; +import org.teavm.model.transformation.NullCheckInsertion; import org.teavm.model.util.AsyncMethodFinder; import org.teavm.runtime.Allocator; import org.teavm.runtime.CallSite; @@ -220,7 +221,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed); - nullCheckInsertion = new NullCheckInsertion(characteristics); + nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics)); nullCheckTransformation = new NullCheckTransformation(); controller.addVirtualMethods(VIRTUAL_METHODS::contains); diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 9dd0ae501..c3adb96b5 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -95,7 +95,9 @@ import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.RaiseInstruction; import org.teavm.model.instructions.StringConstantInstruction; -import org.teavm.model.lowlevel.BoundCheckInsertion; +import org.teavm.model.transformation.BoundCheckInsertion; +import org.teavm.model.transformation.NullCheckFilter; +import org.teavm.model.transformation.NullCheckInsertion; import org.teavm.model.util.AsyncMethodFinder; import org.teavm.model.util.ProgramUtils; import org.teavm.vm.BuildTarget; @@ -128,6 +130,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { private int topLevelNameLimit = 10000; private AstDependencyExtractor dependencyExtractor = new AstDependencyExtractor(); private boolean strict; + private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion(); + private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY); @Override public List getTransformers() { @@ -339,7 +343,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { @Override public void beforeOptimizations(Program program, MethodReader method) { if (strict) { - new BoundCheckInsertion().transformProgram(program, method.getReference()); + boundCheckInsertion.transformProgram(program, method.getReference()); + nullCheckInsertion.transformProgram(program, method.getReference()); } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Precedence.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Precedence.java index d28825597..087f274f0 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Precedence.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Precedence.java @@ -30,6 +30,7 @@ public enum Precedence { ADDITION, MULTIPLICATION, UNARY, + NEW, FUNCTION_CALL, MEMBER_ACCESS, GROUPING; diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 369a78ba5..977b210b3 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -1113,6 +1113,11 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (injector != null) { injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod()); } else { + Precedence outerPrecedence = precedence; + if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) { + writer.append('('); + } + if (expr.getType() == InvocationType.DYNAMIC) { precedence = Precedence.MEMBER_ACCESS; expr.getArguments().get(0).acceptVisitor(this); @@ -1184,6 +1189,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (shouldEraseCallSite) { lastCallSite = null; } + + if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) { + writer.append(')'); + } } if (expr.getLocation() != null) { popLocation(); @@ -1224,14 +1233,14 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { } Precedence outerPrecedence = precedence; - if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) { + if (outerPrecedence.ordinal() > Precedence.NEW.ordinal()) { writer.append('('); } - precedence = Precedence.FUNCTION_CALL; + precedence = Precedence.NEW; writer.append("new ").appendClass(expr.getConstructedClass()); - if (outerPrecedence.ordinal() > Precedence.FUNCTION_CALL.ordinal()) { + if (outerPrecedence.ordinal() > Precedence.NEW.ordinal()) { writer.append(')'); } diff --git a/core/src/main/java/org/teavm/model/lowlevel/LowLevelNullCheckFilter.java b/core/src/main/java/org/teavm/model/lowlevel/LowLevelNullCheckFilter.java new file mode 100644 index 000000000..acaa87625 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/LowLevelNullCheckFilter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2019 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.lowlevel; + +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.model.transformation.NullCheckFilter; + +public class LowLevelNullCheckFilter implements NullCheckFilter { + private Characteristics characteristics; + + public LowLevelNullCheckFilter(Characteristics characteristics) { + this.characteristics = characteristics; + } + + @Override + public boolean apply(FieldReference field) { + return !characteristics.isStructure(field.getClassName()); + } + + @Override + public boolean apply(MethodReference method) { + return characteristics.isManaged(method.getClassName()) && characteristics.isManaged(method); + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/BoundCheckInsertion.java b/core/src/main/java/org/teavm/model/transformation/BoundCheckInsertion.java similarity index 99% rename from core/src/main/java/org/teavm/model/lowlevel/BoundCheckInsertion.java rename to core/src/main/java/org/teavm/model/transformation/BoundCheckInsertion.java index 63ecfe56f..37f176df4 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/BoundCheckInsertion.java +++ b/core/src/main/java/org/teavm/model/transformation/BoundCheckInsertion.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 konsoletyper. + * Copyright 2019 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.model.lowlevel; +package org.teavm.model.transformation; import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.IntHashSet; diff --git a/core/src/main/java/org/teavm/model/transformation/NullCheckFilter.java b/core/src/main/java/org/teavm/model/transformation/NullCheckFilter.java new file mode 100644 index 000000000..91912e5dc --- /dev/null +++ b/core/src/main/java/org/teavm/model/transformation/NullCheckFilter.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 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.transformation; + +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; + +public interface NullCheckFilter { + boolean apply(FieldReference field); + + boolean apply(MethodReference method); + + NullCheckFilter EMPTY = new NullCheckFilter() { + @Override + public boolean apply(FieldReference field) { + return true; + } + + @Override + public boolean apply(MethodReference method) { + return true; + } + }; +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java b/core/src/main/java/org/teavm/model/transformation/NullCheckInsertion.java similarity index 90% rename from core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java rename to core/src/main/java/org/teavm/model/transformation/NullCheckInsertion.java index 4c8d1d5a0..133452c0a 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/NullCheckInsertion.java +++ b/core/src/main/java/org/teavm/model/transformation/NullCheckInsertion.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Alexey Andreev. + * Copyright 2019 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.model.lowlevel; +package org.teavm.model.transformation; import com.carrotsearch.hppc.IntHashSet; import com.carrotsearch.hppc.IntSet; @@ -39,14 +39,14 @@ import org.teavm.model.util.DominatorWalkerCallback; import org.teavm.model.util.PhiUpdater; public class NullCheckInsertion { - private Characteristics characteristics; + private NullCheckFilter filter; - public NullCheckInsertion(Characteristics characteristics) { - this.characteristics = characteristics; + public NullCheckInsertion(NullCheckFilter filter) { + this.filter = filter; } public void transformProgram(Program program, MethodReference methodReference) { - if (!characteristics.isManaged(methodReference) || program.basicBlockCount() == 0) { + if (!filter.apply(methodReference) || program.basicBlockCount() == 0) { return; } @@ -99,14 +99,14 @@ public class NullCheckInsertion { @Override public void visit(GetFieldInstruction insn) { - if (!characteristics.isStructure(insn.getField().getClassName())) { + if (filter.apply(insn.getField())) { addGuard(insn, GetFieldInstruction::getInstance, GetFieldInstruction::setInstance); } } @Override public void visit(PutFieldInstruction insn) { - if (!characteristics.isStructure(insn.getField().getClassName())) { + if (filter.apply(insn.getField())) { addGuard(insn, PutFieldInstruction::getInstance, PutFieldInstruction::setInstance); } } @@ -123,8 +123,7 @@ public class NullCheckInsertion { @Override public void visit(InvokeInstruction insn) { - if (!characteristics.isStructure(insn.getMethod().getClassName()) - && characteristics.isManaged(insn.getMethod())) { + if (filter.apply(insn.getMethod())) { addGuard(insn, InvokeInstruction::getInstance, InvokeInstruction::setInstance); } }