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

时间:2016-07-03 18:08:26

标签: javascript java nashorn

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

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实现。不知道这里出了什么问题。我确实尝试添加对&#39; toString()&#39;显式在getMember()方法调用中,但没有解决问题。

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

1 个答案:

答案 0 :(得分:2)

来自脚本的

print或“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)");
  }
}