From 0369d100c6236727ac53e201a549a8d4adfe46ab Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 25 Dec 2015 19:04:54 +0300 Subject: [PATCH] Make JSBody report about wrong parameter/returning type/instance type to avoid confusion like this: https://github.com/konsoletyper/teavm/issues/164 --- .../org/teavm/jso/impl/JSClassProcessor.java | 28 +++++ .../test/java/org/teavm/tests/JSOTest.java | 106 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 tests/src/test/java/org/teavm/tests/JSOTest.java diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index 61af97332..b67e0026f 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -356,6 +356,34 @@ class JSClassProcessor { private boolean processJSBodyInvocation(MethodReader method, CallLocation callLocation, InvokeInstruction invoke, MethodHolder methodToProcess) { + boolean valid = true; + for (int i = 0; i < method.parameterCount(); ++i) { + ValueType arg = method.parameterType(i); + if (!typeHelper.isSupportedType(arg)) { + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method " + + " declaration. Its parameter #" + (i + 1) + " has invalid type {{t1}}", invoke.getMethod(), + arg); + valid = false; + } + } + if (invoke.getInstance() != null) { + if (!typeHelper.isSupportedType(ValueType.object(method.getOwnerName()))) { + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method " + + " declaration. It is non-static and declared on a non-overlay class {{c1}}", + invoke.getMethod(), method.getOwnerName()); + valid = false; + } + } + if (method.getResultType() != ValueType.VOID && !typeHelper.isSupportedType(method.getResultType())) { + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method " + + " declaration, since it returns invalid type {{t1}}", invoke.getMethod(), + method.getResultType()); + valid = false; + } + if (!valid) { + return false; + } + requireJSBody(diagnostics, method); MethodReference delegate = repository.methodMap.get(method.getReference()); if (delegate == null) { diff --git a/tests/src/test/java/org/teavm/tests/JSOTest.java b/tests/src/test/java/org/teavm/tests/JSOTest.java new file mode 100644 index 000000000..3c79555b3 --- /dev/null +++ b/tests/src/test/java/org/teavm/tests/JSOTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 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.tests; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import java.util.List; +import org.junit.Test; +import org.teavm.diagnostics.Problem; +import org.teavm.jso.JSBody; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.vm.TeaVM; +import org.teavm.vm.TeaVMBuilder; + +/** + * + * @author Alexey Andreev + */ +public class JSOTest { + @Test + public void reportsAboutWrongParameterOfJSBody() { + Problem foundProblem = build("callJSBodyWithWrongParameter").stream().filter(problem -> { + return problem.getLocation().getMethod().getName().equals("callJSBodyWithWrongParameter") + && problem.getText().equals("Method {{m0}} is not a proper native JavaScript method " + + " declaration. Its parameter #1 has invalid type {{t1}}"); + }).findAny().orElse(null); + + assertNotNull(foundProblem); + Object[] params = foundProblem.getParams(); + assertThat(params[0], is(new MethodReference(JSOTest.class, "jsBodyWithWrongParameter", + Object.class, void.class))); + assertThat(params[1], is(ValueType.parse(Object.class))); + } + + private static void callJSBodyWithWrongParameter() { + jsBodyWithWrongParameter(23); + } + + @JSBody(params = "param", script = "alert(param.toString());") + private static native void jsBodyWithWrongParameter(Object param); + + @Test + public void reportsAboutWrongNonStaticJSBody() { + Problem foundProblem = build("callWrongNonStaticJSBody").stream().filter(problem -> { + return problem.getLocation().getMethod().getName().equals("callWrongNonStaticJSBody") + && problem.getText().equals("Method {{m0}} is not a proper native JavaScript method " + + " declaration. It is non-static and declared on a non-overlay class {{c1}}"); + }).findAny().orElse(null); + + assertNotNull(foundProblem); + Object[] params = foundProblem.getParams(); + assertThat(params[0], is(new MethodReference(JSOTest.class, "wrongNonStaticJSBody", void.class))); + assertThat(params[1], is(JSOTest.class.getName())); + } + + private static void callWrongNonStaticJSBody() { + new JSOTest().wrongNonStaticJSBody(); + } + + @JSBody(params = {}, script = "alert(this.toString());") + private native void wrongNonStaticJSBody(); + + @Test + public void reportsAboutJSBodyWithWrongReturningType() { + Problem foundProblem = build("callJSBodyWithWrongReturningType").stream().filter(problem -> { + return problem.getLocation().getMethod().getName().equals("callJSBodyWithWrongReturningType") + && problem.getText().equals("Method {{m0}} is not a proper native JavaScript method " + + " declaration, since it returns invalid type {{t1}}"); + }).findAny().orElse(null); + + assertNotNull(foundProblem); + Object[] params = foundProblem.getParams(); + assertThat(params[0], is(new MethodReference(JSOTest.class, "jsBodyWithWrongReturningType", String.class, + Object.class))); + assertThat(params[1], is(ValueType.parse(Object.class))); + } + + private static void callJSBodyWithWrongReturningType() { + jsBodyWithWrongReturningType("foo"); + } + + @JSBody(params = "value", script = "return value;") + private static native Object jsBodyWithWrongReturningType(String value); + + private List build(String methodName) { + TeaVM vm = new TeaVMBuilder().build(); + vm.installPlugins(); + vm.entryPoint("test", new MethodReference(JSOTest.class, methodName, void.class)); + vm.build(new StringBuilder(), null); + return vm.getProblemProvider().getSevereProblems(); + } +}