【发布时间】:2022-10-24 21:11:55
【问题描述】:
我知道阅读这么大的帖子有很多要求,但我希望你能这样做,因为我真的不知道从哪里开始寻找我什至不理解的问题的解决方案。
我在 Windows 服务器上有一个 java 服务。该服务在启动时加载/评估几个 javascript 文件。这些 javascript 文件都具有相同的结构,它们包含两个函数:process(name, content) { return { result: data } } 和 selfTest()。服务器加载脚本,评估它,检查所需的函数,运行自检并将process函数的句柄存储在映射中(映射的键是执行脚本的字段名称)如果自检成功:
// Called on MainThread
private static final String JS_FN_SELFTEST = "selfTest";
private static final String JS_FN_PROCESS = "process";
private final Map<String, Value> _cache;
private final Context _ctx;
public ScriptManager () {
_cache = new HashMap<>();
_ctx = Context.newBuilder("js").allowIO(true).build();
}
private void addScriptFile(Path fileName, String... fieldNames)
throws IOException, NoSuchMethodException {
var p = Path.of(fileName.toString());
var jsSource = Files.readString(p);
var evaluated = _ctx.eval("js", jsSource);
if (!evaluated.hasMember(JS_FN_SELFTEST)) {
throw new NoSuchMethodException(JS_FN_SELFTEST);
}
var fnSelfTest = evaluated.getMember(JS_FN_SELFTEST);
fnSelfTest.execute();
if (!evaluated.hasMember(JS_FN_PROCESS)) {
throw new NoSuchMethodException(JS_FN_PROCESS);
}
for(String column: fieldNames) {
_cache.put(column, evaluated.getMember(JS_FN_PROCESS));
}
}
启动后,该服务会监视网络共享中的文件丢失。当文件丢弃时,将读取并处理该文件。数据将首先使用通用脚本进行清理,然后作为地图一部分的字段也将被处理。以下功能进行清洁,现场处理类似,问题也出现在那里。
// Called on JobProcessingThread
public List<Data> clean(DataMap dataSet) {
var modified = new ArrayList<Data>();
var fnProcess = _cache.get(ALL_COLUMNS_MARKER);
for(var key: dataSet.keyList()) {
var data = dataSet.get(key);
if (!data.Field.Scriptable) continue;
String oldContent = data.getContent();
if (oldContent == null) continue;
try {
var value = fnProcess.execute(data.SourceName, oldContent); // error occurs here
var map = value.as(Map.class);
var newContent = map.get("result").toString();
if (!oldContent.equals(newContent)) {
data.setContent(newContent);
modified.add(data);
}
} catch(PolyglotException pe) {
data.setProblem(pe.getMessage());
}
}
return modified;
}
最后这是加载的脚本之一:
(function() {
function process(name, input) {
if (input === null) return { result: null };
let tmp = input.trim();
if (tmp.length === 0) return { result: null };
// some logic here
return {
result: result !== '' ? result : null
};
}
function selfTest() {
}
return { process, selfTest };
})();
就是这样的情景。当我运行应用程序并删除文件时,当第一个可编写脚本的字段在 fnProcess.execute(data.SourceName, oldContent); 行中处理并且 JVM 被终止(!)时,我收到了 StackOverflowError。 netbeans 的脚本调试器显示这发生在let tmp = input.trim(); 行中。当我删除trim() 命令时,脚本将被执行,直到对字符串的下一个函数调用(如indexOf),但长度检查tmp.length === 0 将正常执行(至少netbeans 调试器显示它是这样的)。无论传递的字符串内容如何,都会出现问题,即使是硬编码字符串也会在服务器环境中生成错误。
Uncaught exception exiting JVMCIEnv scope entered at c:\buildbot\worker\e\main\jdk\src\hotspot\share\jvmci\jvmciCompilerToVM.cpp:2386
Exception in thread "Thread-JobCenter": java.lang.StackOverflowError
java.lang.StackOverflowError
at com.oracle.svm.core.graal.snippets.StackOverflowCheckSnippets.newStackOverflowError0(StackOverflowCheckImpl.java:363)
at com.oracle.svm.core.graal.snippets.StackOverflowCheckSnippets.newStackOverflowError(StackOverflowCheckImpl.java:359)
at com.oracle.svm.core.graal.snippets.StackOverflowCheckSnippets.throwNewStackOverflowError(StackOverflowCheckImpl.java:339)
at com.oracle.svm.core.graal.jdk.ArraycopySnippets.boundsCheck(ArraycopySnippets.java:117)
at com.oracle.svm.core.graal.jdk.ArraycopySnippets.doArraycopy(ArraycopySnippets.java:94)
at java.util.Arrays.copyOf(Arrays.java:3539)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:228)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:802)
at java.lang.StringBuilder.append(StringBuilder.java:242)
at com.oracle.svm.core.util.Utf8.utf8ToString(Utf8.java:144)
at com.oracle.svm.core.util.Utf8.utf8ToString(Utf8.java:187)
at com.oracle.svm.jni.functions.JNIFunctions.NewStringUTF(JNIFunctions.java:479)
at jdk.vm.ci.hotspot.CompilerToVM.getResolvedJavaType0(CompilerToVM.java)
at jdk.vm.ci.hotspot.CompilerToVM.getResolvedJavaType(CompilerToVM.java:674)
at jdk.vm.ci.hotspot.HotSpotResolvedJavaMethodImpl.getHolder(HotSpotResolvedJavaMethodImpl.java:97)
at jdk.vm.ci.hotspot.HotSpotResolvedJavaMethodImpl.fromMetaspace(HotSpotResolvedJavaMethodImpl.java:115)
at com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VA_LIST:Ljdk_vm_ci_hotspot_HotSpotResolvedJavaMethodImpl_2_0002efromMetaspace_00028J_00029Ljdk_vm_ci_hotspot_HotSpotResolvedJavaMethod_2(JNIJavaCallWrappers.java:0)
at org.graalvm.libgraal.jni.JNI$CallStaticObjectMethodA.call(JNI.java)
at org.graalvm.libgraal.jni.FromLibGraalCalls.callJObject(FromLibGraalCalls.java:153)
at org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSTruffleCompilerRuntimeGen.callGetTruffleCallBoundaryMethods(HSTruffleCompilerRuntimeGen.java:181)
at org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSTruffleCompilerRuntime.getTruffleCallBoundaryMethods(HSTruffleCompilerRuntime.java:356)
at org.graalvm.compiler.truffle.compiler.hotspot.HotSpotTruffleCompilerImpl.installTruffleCallBoundaryMethods(HotSpotTruffleCompilerImpl.java:216)
at org.graalvm.compiler.truffle.compiler.hotspot.libgraal.TruffleToLibGraalEntryPoints.installTruffleCallBoundaryMethods(TruffleToLibGraalEntryPoints.java:305)
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (jvmciRuntime.cpp:1215), pid=15740, tid=10088
# fatal error: Fatal exception in JVMCI: Uncaught exception exiting JVMCIEnv scope entered at c:\buildbot\worker\e\main\jdk\src\hotspot\share\jvmci\jvmciCompilerToVM.cpp:2386
#
# JRE version: OpenJDK Runtime Environment GraalVM CE 21.1.0 (16.0.1+9) (build 16.0.1+9-jvmci-21.1-b05)
# Java VM: OpenJDK 64-Bit Server VM GraalVM CE 21.1.0 (16.0.1+9-jvmci-21.1-b05, mixed mode, sharing, tiered, jvmci, jvmci compiler, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Data\hs_err_pid15740.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/oracle/graal/issues
#
这是hs_err_pid15740.log 的一部分
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j jdk.vm.ci.hotspot.CompilerToVM.translate(Ljava/lang/Object;)J+0 jdk.internal.vm.ci@16.0.1
j jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.translate(Ljava/lang/Object;)J+5 jdk.internal.vm.ci@16.0.1
j jdk.internal.reflect.GeneratedMethodAccessor4.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+40 java.base@16.0.1
j jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6 java.base@16.0.1
j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+59 java.base@16.0.1
j org.graalvm.libgraal.LibGraal.translate(Ljava/lang/Object;)J+28 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.hotspot.libgraal.TruffleFromLibGraalEntryPoints.getTruffleCallBoundaryMethods(Ljava/lang/Object;)[J+122 jdk.internal.vm.compiler
v ~StubRoutines::call_stub
j org.graalvm.compiler.truffle.runtime.hotspot.libgraal.TruffleToLibGraalCalls.installTruffleCallBoundaryMethods(JJLorg/graalvm/compiler/truffle/common/CompilableTruffleAST;)V+0 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.hotspot.libgraal.LibGraalHotSpotTruffleCompiler.installTruffleCallBoundaryMethods(Lorg/graalvm/compiler/truffle/common/CompilableTruffleAST;)V+25 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.hotspot.AbstractHotSpotTruffleRuntime.bypassedInstalledCode(Lorg/graalvm/compiler/truffle/runtime/OptimizedCallTarget;)V+14 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.interpreterCall()Z+11 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callBoundary([Ljava/lang/Object;)Ljava/lang/Object;+1 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.doInvoke([Ljava/lang/Object;)Ljava/lang/Object;+2 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.callBoundary([Ljava/lang/Object;)Ljava/lang/Object;+9 jdk.internal.vm.compiler
j org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.doInvoke([Ljava/lang/Object;)Ljava/lang/Object;+2 jdk.internal.vm.compiler
// repeated for about 8000 rows...
但是,当我使用与上面相同的脚本文件运行以下代码并将相同的参数传递给 javascript 函数时,就像服务器在文件删除时所做的那样,不会引发异常:
var ctx = Context.newBuilder("js").allowAllAccess(false).allowIO(true).build();
var js = java.nio.file.Files.readString(Path.of("scripts/0.js"));
var evaluated = ctx.eval("js", js);
var fn = evaluated.getMember("process");
var result = fn.execute("test", "some content ");
我希望有人能在巨大的文字墙中幸存下来并坚持到这里并能告诉我我做错了什么......
【问题讨论】:
标签: javascript java graalvm graaljs