在打印JSObject实现时,Nashorn运行时异常

Nashorn runtime exception while printing JSObject implementations

本文关键字:Nashorn 运行时 异常 打印 JSObject 实现      更新时间:2023-09-26

我有一个bean实现了JSObject (&映射)接口如下所示。为了便于阅读,我删除了一些重写的方法。

package test.nashorn;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import jdk.nashorn.api.scripting.JSObject;
public class JSBean implements JSObject, Map<String,Object>{
    /**
     * The current values for this object.
     */
    private HashMap<String, Object> values = new HashMap<>();
    @Override
    public String toString() {
        System.out.println("ToString");
        Set<Entry<String,Object>> entries = values.entrySet();
        StringBuilder sb = new StringBuilder();
        for(Entry<String,Object> entry:entries){
            sb.append(entry.getKey()+ " "+(String)entry.getValue());
        }
        System.out.println("Completed ToString");
        return sb.toString();
    }
    @Override
    public boolean hasMember(String name) {
        return has(name);
    }
    // get the value of that named property
    @Override
    public Object getMember(String name) {
        return get(name);
    }
    // get the value of that named property
    @Override
    public void setMember(String name,Object value) {
         put(name,value);
    }
    public Object get(String name) {
        System.out.println("JAVA Get is called."+name);
         System.out.println("Called for this"+name+" and returned"
         +":"+values.get(name));
        return values.get(name);
    }
    @Override
    public Object put(String name, Object value) {
        System.out.println("JAVA Put is called. Input name: " + name + "'n Input values: " + value);
        return values.put(name, value);
    }
    public boolean has(String name) {
        System.out.println("JAVA Has is called. Input name: " + name);
        return values.containsKey(name);
    }

    public JSBean() {
        // TODO Auto-generated constructor stub
    }
    @Override
    public Object call(Object arg0, Object... arg1) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public Object eval(String arg0) {
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public String getClassName() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object getSlot(int arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean hasSlot(int arg0) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean isArray() {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean isFunction() {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean isInstance(Object arg0) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean isInstanceOf(Object arg0) {
        // TODO Auto-generated method stub
        return false;
    }
    @Override
    public boolean isStrictFunction() {
        // TODO Auto-generated method stub
        return false;
    }

}

当我运行如下所示的测试时

@Test
public void testDefaultValMethod(){
    JSBean bean = new JSBean();
    bean.setMember("hello", " Sport ");
    //Add stuff to engine.
    engine.put("jsBean", bean);
    String source = "(function(){'n"
            + "print(jsBean);"
            + "} )();";
    Object obj=null;
    try {
        obj = engine.eval(source);
    } catch (ScriptException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println("Returned : " + String.valueOf(obj));
}

我在控制台中看到以下错误。理想情况下,Nashorn应该直接调用bean的toString()方法来获得String实现。不知道这里出了什么问题。我确实尝试在getMember()方法调用中显式地添加对'toString()'的调用,但这并没有解决问题。

JAVA Put is called. Input name: hello
 Input values:  Sport 
JAVA Get is called.toString
Called for thistoString and returned:null
JAVA Get is called.valueOf
Called for thisvalueOf and returned:null
javax.script.ScriptException: TypeError: cannot.get.default.string in <eval> at line number 2
    at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:467)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:451)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:403)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399)
    at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
    at test.nashorn.NashornTest.testDefaultValMethod(NashornTest.java:386)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: <eval>:2 TypeError: cannot.get.default.string
    at jdk.nashorn.internal.runtime.JSType.toPrimitive(JSType.java:514)
    at jdk.nashorn.internal.runtime.JSType.toPrimitive(JSType.java:480)
    at jdk.nashorn.internal.runtime.JSType.toStringImpl(JSType.java:1391)
    at jdk.nashorn.internal.runtime.JSType.toString(JSType.java:589)
    at jdk.nashorn.internal.objects.Global.printImpl(Global.java:2782)
    at jdk.nashorn.internal.objects.Global.println(Global.java:1497)
    at jdk.nashorn.internal.scripts.Script$Recompilation$1$11$'^eval'_.L:1(<eval>:2)
    at jdk.nashorn.internal.scripts.Script$'^eval'_.:program(<eval>:1)
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640)
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:446)
    ... 29 more
Caused by: java.lang.UnsupportedOperationException: cannot.get.default.string
    at jdk.nashorn.api.scripting.DefaultValueImpl.getDefaultValue(DefaultValueImpl.java:53)
    at jdk.nashorn.api.scripting.AbstractJSObject.getDefaultValue(AbstractJSObject.java:289)
    at jdk.nashorn.internal.runtime.JSType.toPrimitive(JSType.java:512)
    ... 40 more
Returned : null

打印或"toString"转换从脚本调用"toString"方法对脚本对象。JSObject上的任何属性访问(包括函数值属性)都被路由到getMember方法。因此,要使"valueOf"或"toString",你必须在JSObject子类型中实现适当的getMember。

的例子:

import jdk.nashorn.api.scripting.*;
import javax.script.*;
public class Main {
  static class MyJSObject extends AbstractJSObject {
    @Override
    public Object getMember(String name) {
      if (name.equals("toString")) {
          // return a "function" object for "toString" property
          return new AbstractJSObject() {
              @Override
              public boolean isFunction() {
                  return true;
              }
              @Override
              public Object call(Object self, Object...args) {
                  return self.toString();
              }
          };
      }
      return null; // other properties here
    }
    @Override 
    public String toString() {
        return "my js object";
    }
  }
  public static void main(String[] a) throws Exception {
    ScriptEngineManager m = new ScriptEngineManager();
    ScriptEngine e = m.getEngineByName("nashorn");
    e.put("myObj", new MyJSObject());
    e.eval("print(myObj)");
  }
}
或者,您也可以重写

Object getDefaultValue(final Class<?> hint) throws UnsupportedOperationException

方法在AbstractJSObject子类中。

import jdk.nashorn.api.scripting.*;
import javax.script.*;
public class Main2 {
  static class MyJSObject extends AbstractJSObject {
    @Override
    public Object getDefaultValue(Class<?> hint) {
        if (hint == String.class) {
            return toString();
        }
        throw new UnsupportedOperationException("no conversion for " + hint);
    }
    @Override 
    public String toString() {
        return "my js object";
    }
  }
  public static void main(String[] a) throws Exception {
    ScriptEngineManager m = new ScriptEngineManager();
    ScriptEngine e = m.getEngineByName("nashorn");
    e.put("myObj", new MyJSObject());
    e.eval("print(myObj)");
  }
}