This commit is contained in:
2025-08-27 19:58:59 +08:00
parent eba4f35265
commit 55b79af54b

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2023-2025, Agents-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.agentsflex.core.util;
import com.agentsflex.core.chain.Chain;
import com.agentsflex.core.util.graalvm.JsInteropUtils;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.Value;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class JsConditionUtil {
// 使用 Context.Builder 构建上下文,线程安全
private static final Context.Builder CONTEXT_BUILDER = Context.newBuilder("js")
.option("engine.WarnInterpreterOnly", "false")
.allowHostAccess(HostAccess.ALL) // 允许访问 Java 对象的方法和字段
.allowHostClassLookup(className -> false) // 禁止动态加载任意 Java 类
.option("js.ecmascript-version", "2021"); // 使用较新的 ECMAScript 版本
/**
* 执行 JavaScript 表达式并返回 boolean 结果
*
* @param code JS 表达式(应返回布尔或可转换为布尔的值)
* @param chain Chain 上下文对象
* @param initMap 初始变量映射
* @return true 表示满足条件继续执行false 表示跳过
*/
public static boolean eval(String code, Chain chain, Map<String, Object> initMap) {
try (Context context = CONTEXT_BUILDER.build()) {
Map<String, Object> _result = new HashMap<>();
Value bindings = context.getBindings("js");
// 合并上下文变量
Map<String, Object> contextVariables = collectContextVariables(chain, initMap);
contextVariables.forEach((key, value) -> {
bindings.putMember(key, JsInteropUtils.wrapJavaValueForJS(context, value));
});
bindings.putMember("_result", _result);
code = "_result.value = " + code;
context.eval("js", code);
Object value = _result.get("value");
return toBoolean(value);
} catch (Exception e) {
throw new RuntimeException("JavaScript 执行失败: " + e.getMessage(), e);
}
}
/**
* 收集上下文中的变量
*/
private static Map<String, Object> collectContextVariables(Chain chain, Map<String, Object> initMap) {
Map<String, Object> variables = new ConcurrentHashMap<>();
// 添加 Chain Memory 中的变量(去掉前缀)
chain.getMemory().getAll().forEach((key, value) -> {
int dotIndex = key.indexOf(".");
String varName = (dotIndex >= 0) ? key.substring(dotIndex + 1) : key;
variables.put(varName, value);
});
// 添加 _chain 和 initMap 变量
variables.putAll(initMap);
return variables;
}
/**
* 将任意对象转换为布尔值
*/
private static boolean toBoolean(Object value) {
if (value == null) {
return false;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return ((Number) value).intValue() != 0;
}
if (value instanceof String) {
String str = ((String) value).trim().toLowerCase();
return !str.isEmpty() && !"0".equals(str) && !"false".equals(str);
}
if (value instanceof Value) {
Value v = (Value) value;
if (v.isBoolean()) {
return v.asBoolean();
} else if (v.isNumber()) {
return v.asDouble() != 0;
} else if (v.isString()) {
String str = v.asString().trim().toLowerCase();
return !str.isEmpty() && !"0".equals(str) && !"false".equals(str);
} else {
return !v.isNull();
}
}
return true;
}
}