001/** 002 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.gnu.org/licenses/lgpl-3.0.txt 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package dev.tinyflow.core.util.graalvm; 017 018import org.graalvm.polyglot.Context; 019import org.graalvm.polyglot.Value; 020 021import java.time.*; 022import java.util.*; 023import java.util.stream.Collectors; 024import java.util.stream.IntStream; 025 026public class JsInteropUtils { 027 028 public static Value wrapJavaValueForJS(Context context, Object value) { 029 if (value == null) { 030 return context.asValue(null); 031 } 032 033 // 处理 LocalDateTime / LocalDate / ZonedDateTime -> JS Date 034 if (value instanceof LocalDateTime) { 035 return context.eval("js", "new Date('" + ((LocalDateTime) value).atZone(ZoneId.systemDefault()) + "')"); 036 } 037 if (value instanceof LocalDate) { 038 return context.eval("js", "new Date('" + ((LocalDate) value).atStartOfDay(ZoneId.systemDefault()) + "')"); 039 } 040 if (value instanceof ZonedDateTime) { 041 return context.eval("js", "new Date('" + (value) + "')"); 042 } 043 if (value instanceof Date) { 044 return context.eval("js", "new Date(" + ((Date) value).getTime() + ")"); 045 } 046 047 // 处理 Map -> ProxyObject 048 if (value instanceof Map) { 049 return context.asValue(new ProxyMap((Map) value, context)); 050 } 051 052 // 处理 List -> ProxyArray 053 if (value instanceof List) { 054 return context.asValue(new ProxyList((List) value, context)); 055 } 056 057 // 处理 Set -> ProxyArray 058 if (value instanceof Set) { 059 return context.asValue(new ProxyList(new ArrayList<>((Set) value), context)); 060 } 061 062 // 处理数组 -> ProxyArray 063 if (value.getClass().isArray()) { 064 int length = java.lang.reflect.Array.getLength(value); 065 List<Object> list = IntStream.range(0, length) 066 .mapToObj(i -> java.lang.reflect.Array.get(value, i)) 067 .collect(Collectors.toList()); 068 return context.asValue(new ProxyList(list, context)); 069 } 070 071 // 默认处理:基本类型或 Java 对象直接返回 072 return context.asValue(value); 073 } 074 075 076 // 双向转换:将 JS 值转为 Java 类型 077 public static Object unwrapJsValue(Value value) { 078 if (value.isHostObject()) { 079 return value.asHostObject(); 080 } else if (value.hasMembers()) { 081 Map<String, Object> map = new HashMap<>(); 082 for (String key : value.getMemberKeys()) { 083 map.put(key, unwrapJsValue(value.getMember(key))); 084 } 085 return map; 086 } else if (value.hasArrayElements()) { 087 List<Object> list = new ArrayList<>(); 088 long size = value.getArraySize(); 089 for (long i = 0; i < size; i++) { 090 list.add(unwrapJsValue(value.getArrayElement(i))); 091 } 092 return list; 093 } else if (value.isDate()) { 094 Instant instant = Instant.from(value.asDate()); 095 return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 096 } else { 097 return value.as(Object.class); 098 } 099 } 100 101}