使用 Javascript 函数接受字节数组,使用 GraalVM 和 Java

Using a Javascript function that accepts a byte array using GraalVM and Java

我有一个Java脚本文件: https://gist.github.com/VanitySoft/0cfd26991fa9ac33e03782e4316695c2

// lppDecode decodes an array of bytes into an array of ojects, 
function lppDecode(bytes) {
  //...
}

在我的 Java 测试用例中,我正在尝试发送一个字节数组:

@Test
public void testTestDecorder() throws IOException {
  Context context = Context.create();
  String javaSctiptDecorder = FileUtils.readFileToString(new File("src/main/resources/decoderOnly.js"));
  Value decodeFunction = context.eval("js", javaSctiptDecorder);
  byte[] bytes = "016700E1027329EC038807FDD800BEE10000C8".getBytes();
  String json = decodeFunction.execute(new String(bytes)).asString();
  logger.debug(json);       
}

但我一直收到错误消息:

java.lang.UnsupportedOperationException: Unsupported operation Value.execute(Object...) for 'undefined'(language: JavaScript, type: undefined). You can ensure that the operation is supported using Value.canExecute().
  at com.oracle.truffle.polyglot.PolyglotEngineException.unsupported(PolyglotEngineException.java:144)
  at com.oracle.truffle.polyglot.PolyglotValue.unsupported(PolyglotValue.java:620)
  at com.oracle.truffle.polyglot.PolyglotValue.executeUnsupported(PolyglotValue.java:263)
  at com.oracle.truffle.polyglot.PolyglotValue$InteropCodeCache$AbstractExecuteNode.executeShared(PolyglotValue.java:1906)
  at com.oracle.truffle.polyglot.PolyglotValue$InteropCodeCache$ExecuteNode.executeImpl(PolyglotValue.java:1978)
  at com.oracle.truffle.polyglot.HostToGuestRootNode.execute(HostToGuestRootNode.java:113)
  at com.oracle.truffle.api.impl.DefaultCallTarget.callDirectOrIndirect(DefaultCallTarget.java:84)
  at com.oracle.truffle.api.impl.DefaultCallTarget.call(DefaultCallTarget.java:99)
  at com.oracle.truffle.api.impl.DefaultRuntimeAccessor$DefaultRuntimeSupport.callProfiled(DefaultRuntimeAccessor.java:119)
  at com.oracle.truffle.polyglot.PolyglotValue$InteropValue.execute(PolyglotValue.java:2787)
  at org.graalvm.polyglot.Value.execute(Value.java:457)
  at com.vanitysoft.nearsight.domain.dao.impl.GraavlTest.testTestDecorder(GraavlTest.java:44)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.base/java.lang.reflect.Method.invoke(Method.java:566)
  at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
  at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
  at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
  at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
  at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
  at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
  at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
  at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
  at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
  at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
  at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
  at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
  at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
  at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
  at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
  at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
  at org.junit.vintage.engine.execution.RunnerExecutor.execute(RunnerExecutor.java:43)
  at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
  at java.base/java.util.stream.ReferencePipeline.accept(ReferencePipeline.java:195)
  at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
  at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
  at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
  at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
  at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
  at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
  at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
  at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
  at org.junit.vintage.engine.VintageTestEngine.executeAllChildren(VintageTestEngine.java:82)
  at org.junit.vintage.engine.VintageTestEngine.execute(VintageTestEngine.java:73)
  at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
  at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute(DefaultLauncher.java:211)
  at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
  at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
  at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:141)
  at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
  at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: Attached Guest Language Frames (1)

如何让 GraalVM 使用这个字节数组?

您的 js 代码似乎缺少括号。我一直在您的代码中按原样拼写变量名中的拼写错误。

@Test
public void testTestDecorder() throws IOException {
    Context context = Context.create();
    String javaSctiptDecorder = FileUtils.readFileToString(new File("src/main/resources/decoderOnly.js"));
    javaSctiptDecorder = "(" + javaSctiptDecorder + ")";
    Value decodeFunction = context.eval("js", javaSctiptDecorder);
    byte[] bytes = "016700E1027329EC038807FDD800BEE10000C8".getBytes();
    String json = decodeFunction .execute(new String(bytes)).asString();
    logger.debug(json);     
}

查看这些官方示例:1 and 2
注意 JS objects/functions.

周围的括号

问题是在decoderFunction.js you are defining the named function lppDecode. In Javascript, this is considered a function declaration中,一个语句,而不是一个表达式,Graal 不能计算它。

问题描述在this Github issue. alexjordan's in his answer提供解决方案:

You need to add parentheses around it, otherwise a named function is considered a statement not an expression in JavaScript. (function test() { return 'Hello World' }) should work.

因此,在原始 Java 脚本文件中,或者在构建 Java 变量时,将您的函数括在括号内应该可行,正如 Mythos 在他的回答中所指出的那样。

您可以在浏览器 Java 脚本控制台中检查指示的行为。尝试定义以下内容:

function lppDecode(bytes) { console.log('Bytes:', bytes); return bytes;}

因此,如图所示,浏览器将打印 undefined:

相反,如果您将函数括在括号中:

(function lppDecode(bytes) { console.log('Bytes:', bytes); return bytes;})

您将收到对该函数的引用:

可以直接使用的:

这就是 Graal 会做的。

这些重要的相关参考资料提供了更多信息: