Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion resources/bundles/org.eclipse.core.resources/.classpath
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-25"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" output="bin_ant" path="src_ant"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
org.eclipse.jdt.core.compiler.codegen.targetPlatform=25
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=17
org.eclipse.jdt.core.compiler.compliance=25
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
Expand Down Expand Up @@ -127,7 +127,7 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=17
org.eclipse.jdt.core.compiler.source=25
org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ Require-Bundle: org.eclipse.ant.core;bundle-version="[3.7.0,4.0.0)";resolution:=
Bundle-ActivationPolicy: lazy
Service-Component: OSGI-INF/org.eclipse.core.internal.resources.CheckMissingNaturesListener.xml,
OSGI-INF/org.eclipse.core.internal.resources.ResourceChangeListenerRegistrar.xml
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-RequiredExecutionEnvironment: JavaSE-25
Automatic-Module-Name: org.eclipse.core.resources
Import-Package: com.sun.jna;version="[5.17.0,6.0.0)",
org.eclipse.osgi.service.datalocation;version="[1.4.0,2.0.0)"
Import-Package: org.eclipse.osgi.service.datalocation;version="[1.4.0,2.0.0)"
2 changes: 2 additions & 0 deletions resources/bundles/org.eclipse.core.resources/build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ extra.ant_tasks/resources-ant.jar = platform:/plugin/org.apache.ant, platform:/p
jars.extra.classpath = platform:/plugin/org.apache.ant/lib/ant.jar
javacWarnings..=-unavoidableGenericProblems
javacWarnings.ant_tasks/resources-ant.jar=-unavoidableGenericProblems
javacSource=25
javacTarget=25
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2002, 2024 IBM Corporation and others.
* Copyright (c) 2002, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -12,13 +12,19 @@
* IBM - Initial API and implementation
* Mikael Barbero (Eclipse Foundation) - 286681 handle WAIT_ABANDONED_0 return value
* Hannes Wellmann - Migrate Win32Natives to use JNA instead of JNI to avoid native binaries and platform-specific fragments
* Contributors - Migrate Win32Natives to use FFM (Foreign Function & Memory) API
*******************************************************************************/

package org.eclipse.core.internal.resources.refresh.win32;

import com.sun.jna.Native;
import com.sun.jna.NativeLibrary;
import com.sun.jna.WString;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.nio.charset.StandardCharsets;

/**
* Hooks for native methods involved with win32 auto-refresh callbacks.
Expand Down Expand Up @@ -95,33 +101,111 @@ public class Win32Natives {
public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x10; // winnt.h

private static class WindowsH {
private static final Linker LINKER = Linker.nativeLinker();
private static final SymbolLookup KERNEL32;

// Method handles for Kernel32 functions
private static final MethodHandle FindFirstChangeNotificationW;
private static final MethodHandle FindCloseChangeNotification;
private static final MethodHandle FindNextChangeNotification;
private static final MethodHandle WaitForMultipleObjects;
private static final MethodHandle GetLastError;

static {
Native.register(NativeLibrary
.getInstance("Kernel32" /* , W32APIOptions.DEFAULT_OPTIONS < type-conversion unnecessary> */)); //$NON-NLS-1$
try {
// Load Kernel32 library
KERNEL32 = SymbolLookup.libraryLookup("Kernel32", Arena.global());

// WinNT's type 'HANDLE' is expressed as a pointer (long on 64-bit, int on 32-bit)
// We use ADDRESS layout which represents a native pointer

// HANDLE FindFirstChangeNotificationW(LPCWSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter)
FindFirstChangeNotificationW = LINKER.downcallHandle(
KERNEL32.find("FindFirstChangeNotificationW").orElseThrow(),
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)
);

// BOOL FindCloseChangeNotification(HANDLE hChangeHandle)
FindCloseChangeNotification = LINKER.downcallHandle(
KERNEL32.find("FindCloseChangeNotification").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
);

// BOOL FindNextChangeNotification(HANDLE hChangeHandle)
FindNextChangeNotification = LINKER.downcallHandle(
KERNEL32.find("FindNextChangeNotification").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS)
);

// DWORD WaitForMultipleObjects(DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds)
WaitForMultipleObjects = LINKER.downcallHandle(
KERNEL32.find("WaitForMultipleObjects").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT)
);

// DWORD GetLastError(void)
GetLastError = LINKER.downcallHandle(
KERNEL32.find("GetLastError").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_INT)
);
} catch (Throwable t) {
throw new ExceptionInInitializerError(t);
}
}
// Direct-mappings are faster than interface-mappings
// and avoiding type-conversions is faster again.
// https://github.com/java-native-access/jna/blob/master/www/FunctionalDescription.md#direct-mapping

// WinNT's type 'HANDLE is expressed as long
// (in winnt.h it is defined as 'typedef HANDLE *PHANDLE;')

// Methods from fileapi.h header

static MemorySegment FindFirstChangeNotificationW(MemorySegment lpPathName, int bWatchSubtree, int dwNotifyFilter) {
try {
return (MemorySegment) FindFirstChangeNotificationW.invokeExact(lpPathName, bWatchSubtree, dwNotifyFilter);
} catch (Throwable t) {
throw new RuntimeException("Failed to call FindFirstChangeNotificationW", t);
}
}

static native long FindFirstChangeNotificationW(WString lpPathName, int bWatchSubtree, int dwNotifyFilter);

static native int FindCloseChangeNotification(long hChangeHandle);
static int FindCloseChangeNotification(MemorySegment hChangeHandle) {
try {
return (int) FindCloseChangeNotification.invokeExact(hChangeHandle);
} catch (Throwable t) {
throw new RuntimeException("Failed to call FindCloseChangeNotification", t);
}
}

static native int FindNextChangeNotification(long hChangeHandle);
static int FindNextChangeNotification(MemorySegment hChangeHandle) {
try {
return (int) FindNextChangeNotification.invokeExact(hChangeHandle);
} catch (Throwable t) {
throw new RuntimeException("Failed to call FindNextChangeNotification", t);
}
}

// Methods from synchapi.h

static native int WaitForMultipleObjects(int nCount, long[] lpHandles, int bWaitAll, int dwMilliseconds);
static int WaitForMultipleObjects(int nCount, MemorySegment lpHandles, int bWaitAll, int dwMilliseconds) {
try {
return (int) WaitForMultipleObjects.invokeExact(nCount, lpHandles, bWaitAll, dwMilliseconds);
} catch (Throwable t) {
throw new RuntimeException("Failed to call WaitForMultipleObjects", t);
}
}

static int GetLastError() {
try {
return (int) GetLastError.invokeExact();
} catch (Throwable t) {
throw new RuntimeException("Failed to call GetLastError", t);
}
}

// direct type conversion methods
// Helper methods for type conversion

static WString fromString(String value) {
return new WString(value);
static MemorySegment toWideString(Arena arena, String value) {
// Convert Java String to null-terminated wide string (UTF-16LE)
// In Java 21, we need to manually encode and allocate
byte[] bytes = (value + "\0").getBytes(StandardCharsets.UTF_16LE);
MemorySegment segment = arena.allocate(bytes.length);
segment.asByteBuffer().put(bytes);
return segment;
}

static int fromBoolean(boolean value) {
Expand Down Expand Up @@ -156,9 +240,12 @@ static boolean toBoolean(int value) {
* ERROR_INVALID_HANDLE if the attempt fails.
*/
public static long FindFirstChangeNotification(String lpPathName, boolean bWatchSubtree, int dwNotifyFilter) {
WString wPathName = WindowsH
.fromString(!lpPathName.startsWith(LONG_PATH_PREFIX) ? LONG_PATH_PREFIX + lpPathName : lpPathName);
return WindowsH.FindFirstChangeNotificationW(wPathName, WindowsH.fromBoolean(bWatchSubtree), dwNotifyFilter);
String fullPath = !lpPathName.startsWith(LONG_PATH_PREFIX) ? LONG_PATH_PREFIX + lpPathName : lpPathName;
try (Arena arena = Arena.ofConfined()) {
MemorySegment wPathName = WindowsH.toWideString(arena, fullPath);
MemorySegment handle = WindowsH.FindFirstChangeNotificationW(wPathName, WindowsH.fromBoolean(bWatchSubtree), dwNotifyFilter);
return handle.address();
}
}

/**
Expand All @@ -171,7 +258,8 @@ public static long FindFirstChangeNotification(String lpPathName, boolean bWatch
* otherwise.
*/
public static boolean FindCloseChangeNotification(long hChangeHandle) {
return WindowsH.toBoolean(WindowsH.FindCloseChangeNotification(hChangeHandle));
MemorySegment handle = MemorySegment.ofAddress(hChangeHandle);
return WindowsH.toBoolean(WindowsH.FindCloseChangeNotification(handle));
}

/**
Expand All @@ -183,7 +271,8 @@ public static boolean FindCloseChangeNotification(long hChangeHandle) {
* @return boolean <code>true</code> if the method succeeds, <code>false</code> otherwise.
*/
public static boolean FindNextChangeNotification(long hChangeHandle) {
return WindowsH.toBoolean(WindowsH.FindNextChangeNotification(hChangeHandle));
MemorySegment handle = MemorySegment.ofAddress(hChangeHandle);
return WindowsH.toBoolean(WindowsH.FindNextChangeNotification(handle));
}

/**
Expand All @@ -207,15 +296,23 @@ public static boolean FindNextChangeNotification(long hChangeHandle) {
* function fails.
*/
public static int WaitForMultipleObjects(int nCount, long[] lpHandles, boolean bWaitAll, int dwMilliseconds) {
return WindowsH.WaitForMultipleObjects(nCount, lpHandles, WindowsH.fromBoolean(bWaitAll), dwMilliseconds);
try (Arena arena = Arena.ofConfined()) {
// Allocate memory for array of handles (array of pointers)
long arraySize = ValueLayout.ADDRESS.byteSize() * nCount;
MemorySegment handlesArray = arena.allocate(arraySize, ValueLayout.ADDRESS.byteAlignment());
for (int i = 0; i < nCount; i++) {
handlesArray.setAtIndex(ValueLayout.ADDRESS, i, MemorySegment.ofAddress(lpHandles[i]));
}
return WindowsH.WaitForMultipleObjects(nCount, handlesArray, WindowsH.fromBoolean(bWaitAll), dwMilliseconds);
}
}

/**
* Answers the last error set in the current thread.
* @return int the last error
*/
public static int GetLastError() {
return Native.getLastError();
return WindowsH.GetLastError();
}

}
Loading