Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8191301

JavaImporter fails to resolve imported elements within functions, that contain too many statements

    XMLWordPrintable

Details

    • b36
    • x86_64
    • windows

    Backports

      Description

        FULL PRODUCT VERSION :
        java version "1.8.0_152"
        Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
        Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)


        ADDITIONAL OS VERSION INFORMATION :
        Microsoft Windows [Version 10.0.14393]

        EXTRA RELEVANT SYSTEM CONFIGURATION :
        Using the build-in nashorn script engine.

        A DESCRIPTION OF THE PROBLEM :
        The JavaImporter fails to resolve imported Java elements within functions that contain too many statements. This is best explained by an example. Consider the following script:

         var imports = new JavaImporter(java.lang, java.util);
         with (imports) {
          function func() {
            System.out.println("Hello"); // first statement
            // more statements...
          }
        }

        When this script is evaluated by a nashorn script engine and then the function is invoded from Java side using Invocable.invokeFunction("func") then everything works fine as long as the function does not contain too many statements.

        If the function has more statements than a certain limit then the call will fail with a ScriptException "TypeError: [object Object] is not a JavaImporter object in <eval> at line number 4" indicating an error in the first line of the script function that tries to use Java elements imported by the JavaImporter. The error text may wary depending on the first Java element that should be resolved. E.g. it is "TypeError: function __noSuchMethod__() { [native code] } is not a constructor function in <eval> at line number 4" when the first line is " var i = new Integer(2);"

        The length (characters) of the overall script does not matter.
        The length (characters) of the with(imports) block does not matter.
        The count of elements in the with(imports) block does not matter.
        The length (characters) of the function body does not matter.
        The count of expressions within the function body does not matter.

        Neither matters the count of the code lines. It does not matter if the statements are in separate lines or in a single line.

        I tested different character lengths by using different variable names, longer/shorter String literals and adding/removing javascript comments.

        What matters is the count of javascript statements within the function.

        It is important to note, that the limit strongly depends on the actual statements. count of different Java elements that are referenced within the javascript function. It seams to depend on the count of statements that refer to Java elements as well as on the count of different Java elements that are referred.

        The maximum count of statements increased between the JDK 1.8.0_141 and 1.8.0_152 releases. The following numbers have been determined on my machine by trial-and-error. Not sure if the exact count is machine dependent. The numbers are for 1.8.0_141 and for 1.8.0_152 in parentheses.

        If the function above only refers to System.out.println, then it may contain 744 (1365) statements. It will fail starting with 745 (1366) statements. It will only succeed with 743 (1363) statements if one of them additionally refers to Integer, and so on.
        Even if the function contains only one single reference to a Java element (e.g. System.out) it will fail to resolve it, if the function contains more then 1925 (4678) statements.




        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Retrieve a new ScriptEngine for "javascript" from a new ScriptManager. Evaluate the example script and then invoke the example function from the Java code using Invocable.invokeFunction.

        You can use the example source code provided as JavaImporterBug below.
        If it does not fail for you, try increasing the argument to getScript(int)


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        The script function should be invoked and executed.
        ACTUAL -
        The execution of the function invocation does not even start. Instead. According to the stacktrace the error seams to occur somewhere in Script$Recompilation


        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        Stacktrace:

        Exception in thread "main" javax.script.ScriptException: TypeError: [object Object] is not a JavaImporter object in <eval> at line number 4
        at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470)
        at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:392)
        at jdk.nashorn.api.scripting.NashornScriptEngine.invokeFunction(NashornScriptEngine.java:190)
        at de.expi.scm.scripting.JavaImporterBug.main(JavaImporterBug.java:22)
        Caused by: <eval>:4 TypeError: [object Object] is not a JavaImporter object
        at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57)
        at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:213)
        at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:185)
        at jdk.nashorn.internal.runtime.ECMAErrors.typeError(ECMAErrors.java:172)
        at jdk.nashorn.internal.objects.NativeJavaImporter.__noSuchProperty__(NativeJavaImporter.java:109)
        at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:659)
        at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494)
        at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
        at jdk.nashorn.internal.runtime.ScriptObject.invokeNoSuchProperty(ScriptObject.java:2378)
        at jdk.nashorn.internal.runtime.ScriptObject.megamorphicGet(ScriptObject.java:2016)
        at jdk.nashorn.internal.scripts.Script$Recompilation$2$\^eval\_.func$:split(<eval>:4)
        at jdk.nashorn.internal.scripts.Script$Recompilation$1$80$\^eval\_.func(<eval>:4)
        at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637)
        at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494)
        at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
        at jdk.nashorn.api.scripting.ScriptObjectMirror.callMember(ScriptObjectMirror.java:199)
        at jdk.nashorn.api.scripting.NashornScriptEngine.invokeImpl(NashornScriptEngine.java:386)
        ... 2 more

        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        public class JavaImporterBug
        {
          public static void main(String[] args) throws ScriptException, NoSuchMethodException {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("javascript");

            String script = getScript(1366);
            printScript(script);

            engine.eval(script);
            if (engine instanceof Invocable) {
              ((Invocable) engine).invokeFunction("func");
            }
          }

          private static String getScript(int functionStatementCount) {
            StringWriter script = new StringWriter();
            PrintWriter writer = new PrintWriter(script);
            writer.println("var imports = new JavaImporter(java.lang);");
            writer.println("with (imports) {");
            writer.println(" function func() {");
            writer.println(" System.out.println(\"Hello function\"); // first statement ");
            for (int count = 2; count <= functionStatementCount; count++) {
              writer.println(" System.out.println(\"Hello again\"); // function statement " + count);
            }
            writer.println(" }");
            writer.println("}");
            writer.println();
            return script.toString();
          }

          private static void printScript(String script) {
            int lineNo = 0;
            for (String line : script.split("\\R")) {
              System.out.println(++lineNo + "\t" + line);
            }
          }
        }
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        Reducing the number of statements in the javascript function. Try to extract parts of the (large) function into smaller additional functions and call them from the original function. This off course is only possible if you have control over the source code.

        Attachments

          Issue Links

            Activity

              People

                pmuthuswamy Priya Lakshmi Muthuswamy (Inactive)
                webbuggrp Webbug Group
                Votes:
                0 Vote for this issue
                Watchers:
                6 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: