You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ge...@apache.org on 2018/02/20 08:34:45 UTC

[incubator-netbeans] branch master updated: Adding utilities to construct boot classpath and module boot classpath on both JDK 8 and JDK 9+ (replaces TestUtil.getBootClassPath). Using it in hint tests and completion tests. (#430)

This is an automated email from the ASF dual-hosted git repository.

geertjan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a773b5  Adding utilities to construct boot classpath and module boot classpath on both JDK 8 and JDK 9+ (replaces TestUtil.getBootClassPath). Using it in hint tests and completion tests. (#430)
8a773b5 is described below

commit 8a773b5aa86847acdb2fe3166bf568daa2676bb9
Author: Jan Lahoda <la...@gmail.com>
AuthorDate: Tue Feb 20 09:34:42 2018 +0100

    Adding utilities to construct boot classpath and module boot classpath on both JDK 8 and JDK 9+ (replaces TestUtil.getBootClassPath). Using it in hint tests and completion tests. (#430)
---
 .../java/completion/CompletionTestBase.java        |  13 +-
 java.hints.test/nbproject/project.properties       |   2 +-
 java.hints.test/nbproject/project.xml              |  29 +-
 .../modules/java/hints/test/BootClassPathUtil.java | 307 +++++++++++++++++++++
 .../modules/java/hints/test/api/HintTest.java      |  49 +---
 .../modules/java/hints/test/api/HintTestTest.java  |  18 ++
 java.hints/nbproject/project.xml                   |   3 +
 .../modules/java/source/BootClassPathUtil.java     | 307 +++++++++++++++++++++
 .../org/netbeans/modules/java/source/TestUtil.java |  60 +---
 9 files changed, 682 insertions(+), 106 deletions(-)

diff --git a/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java b/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
index 7e77cfe..217f8a9 100644
--- a/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
+++ b/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
@@ -55,6 +55,7 @@ import org.netbeans.api.lexer.Language;
 import org.netbeans.core.startup.Main;
 import org.netbeans.junit.NbTestCase;
 import org.netbeans.modules.java.JavaDataLoader;
+import org.netbeans.modules.java.source.BootClassPathUtil;
 import org.netbeans.modules.java.source.TestUtil;
 import org.netbeans.modules.java.source.indexing.TransactionContext;
 import org.netbeans.modules.java.source.usages.BinaryAnalyser;
@@ -118,6 +119,7 @@ public class CompletionTestBase extends NbTestCase {
     protected void setUp() throws Exception {
         ClassPathProvider cpp = new ClassPathProvider() {
             volatile ClassPath bootCache;
+            volatile ClassPath moduleBootCache;
             @Override
             public ClassPath findClassPath(FileObject file, String type) {
                 try {
@@ -127,10 +129,17 @@ public class CompletionTestBase extends NbTestCase {
                     if (type.equals(ClassPath.COMPILE)) {
                         return ClassPathSupport.createClassPath(new FileObject[0]);
                     }
-                    if (type.equals(ClassPath.BOOT) || type.equals(JavaClassPathConstants.MODULE_BOOT_PATH)) {
+                    if (type.equals(ClassPath.BOOT)) {
                         ClassPath cp = bootCache;
                         if (cp == null) {
-                            cp = TestUtil.getBootClassPath();
+                            bootCache = cp = BootClassPathUtil.getBootClassPath();
+                        }
+                        return cp;
+                    }
+                    if (type.equals(JavaClassPathConstants.MODULE_BOOT_PATH)) {
+                        ClassPath cp = moduleBootCache;
+                        if (cp == null) {
+                            moduleBootCache = cp = BootClassPathUtil.getModuleBootPath();
                         }
                         return cp;
                     }
diff --git a/java.hints.test/nbproject/project.properties b/java.hints.test/nbproject/project.properties
index 0bc79b3..f211965 100644
--- a/java.hints.test/nbproject/project.properties
+++ b/java.hints.test/nbproject/project.properties
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 is.autoload=true
-javac.source=1.7
+javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
 spec.version.base=1.19.0
 javadoc.arch=${basedir}/arch.xml
diff --git a/java.hints.test/nbproject/project.xml b/java.hints.test/nbproject/project.xml
index 0a5da9d..fc7a21a 100644
--- a/java.hints.test/nbproject/project.xml
+++ b/java.hints.test/nbproject/project.xml
@@ -87,6 +87,13 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.java.j2seplatform</code-name-base>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.42</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -147,6 +154,12 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.parsing.nb</code-name-base>
+                    <run-dependency>
+                        <specification-version>1.4</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.projectapi</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -156,6 +169,12 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi.nb</code-name-base>
+                    <run-dependency>
+                        <specification-version>1.4</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -231,7 +250,7 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util.ui</code-name-base>
+                    <code-name-base>org.openide.util</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
@@ -239,19 +258,19 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util</code-name-base>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>9.3</specification-version>
+                        <specification-version>8.12</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <code-name-base>org.openide.util.ui</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>8.12</specification-version>
+                        <specification-version>9.8</specification-version>
                     </run-dependency>
                 </dependency>
             </module-dependencies>
diff --git a/java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java b/java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java
new file mode 100644
index 0000000..4e44114
--- /dev/null
+++ b/java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.netbeans.modules.java.hints.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.spi.java.classpath.PathResourceImplementation;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.BaseUtilities;
+
+import static junit.framework.TestCase.assertFalse;
+import org.openide.filesystems.MultiFileSystem;
+import org.openide.filesystems.Repository;
+
+/** Note this is duplicated in:
+ * java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java
+ *
+ * @author lahvac
+ */
+public class BootClassPathUtil {
+
+    public static ClassPath getBootClassPath() {
+        String cp = System.getProperty("sun.boot.class.path");
+        if (cp != null) {
+            List<URL> urls = new ArrayList<>();
+            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
+            for (String path : paths) {
+                File f = new File(path);
+
+                if (!f.canRead())
+                    continue;
+
+                FileObject fo = FileUtil.toFileObject(f);
+                if (FileUtil.isArchiveFile(fo)) {
+                    fo = FileUtil.getArchiveRoot(fo);
+                }
+                if (fo != null) {
+                    urls.add(fo.toURL());
+                }
+            }
+            return ClassPathSupport.createClassPath((URL[])urls.toArray(new URL[0]));
+        } else {
+            try {
+                Class.forName("org.netbeans.ProxyURLStreamHandlerFactory").getMethod("register")
+                                                                          .invoke(null);
+            } catch (ClassNotFoundException | NoSuchMethodException |
+                     SecurityException | IllegalAccessException |
+                    IllegalArgumentException | InvocationTargetException ex) {
+                throw new IllegalStateException(ex);
+            }
+            final List<PathResourceImplementation> modules = new ArrayList<>();
+            final File installDir = new File(System.getProperty("java.home"));
+            final URI imageURI = getImageURI(installDir);
+            try {
+                final FileObject jrtRoot = URLMapper.findFileObject(imageURI.toURL());
+                final FileObject root = getModulesRoot(jrtRoot);
+                for (FileObject module : root.getChildren()) {
+                    modules.add(ClassPathSupport.createResource(module.toURL()));
+                }
+            } catch (MalformedURLException e) {
+                throw new IllegalStateException(e);
+            }
+            assertFalse(modules.isEmpty());
+            return ClassPathSupport.createClassPath(modules);
+        }
+    }
+
+    public static ClassPath getModuleBootPath() {
+        if (System.getProperty("sun.boot.class.path") != null) {
+            try {
+                //JDK 8:
+                return getModuleBootOnJDK8();
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            return getBootClassPath();
+        }
+    }
+
+    private static final String PROTOCOL = "nbjrt"; //NOI18N
+
+    private static URI getImageURI(@NonNull final File jdkHome) {
+        try {
+            return new URI(String.format(
+                "%s:%s!/%s",  //NOI18N
+                PROTOCOL,
+                BaseUtilities.toURI(jdkHome).toString(),
+                ""));
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @NonNull
+    private static FileObject getModulesRoot(@NonNull final FileObject jrtRoot) {
+        final FileObject modules = jrtRoot.getFileObject("modules");    //NOI18N
+        //jimage v1 - modules are located in the root
+        //jimage v2 - modules are located in "modules" folder
+        return modules == null ?
+            jrtRoot :
+            modules;
+    }
+
+    //create fake "system module path" while running on JDK 8
+    //java.base contains rt.jar and exports all java.* packages:
+    private static ClassPath moduleBootOnJDK8;
+
+    private static ClassPath getModuleBootOnJDK8() throws Exception {
+        if (moduleBootOnJDK8 == null) {
+            List<FileSystem> roots = new ArrayList<>();
+
+            FileSystem output = FileUtil.createMemoryFileSystem();
+            FileObject sink = output.getRoot();
+
+            roots.add(output);
+
+            Set<String> packages = new HashSet<>();
+            for (FileObject r : getBootClassPath().getRoots()) {
+                FileObject javaDir = r.getFileObject("java");
+
+                if (javaDir == null)
+                    continue;
+
+                roots.add(r.getFileSystem());
+
+                Enumeration<? extends FileObject> c = javaDir.getChildren(true);
+
+                while (c.hasMoreElements()) {
+                    FileObject current = c.nextElement();
+
+                    if (!current.isData() || !current.hasExt("class")) continue;
+
+                    String rel = FileUtil.getRelativePath(r, current.getParent());
+
+                    packages.add(rel.replace('/', '.'));
+                }
+            }
+
+            FileSystem outS = new MultiFileSystem(roots.toArray(new FileSystem[0])) {
+                {
+                    setSystemName("module-boot");
+                }
+            };
+
+            Repository.getDefault().addFileSystem(outS);
+
+            StringBuilder moduleInfo = new StringBuilder();
+
+            moduleInfo.append("module java.base {\n");
+
+            for (String pack : packages) {
+                moduleInfo.append("    exports " + pack + ";\n");
+            }
+
+            moduleInfo.append("}\n");
+
+            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+            FileObject javaBase = outS.getRoot();
+
+            try (JavaFileManager fm = compiler.getStandardFileManager(null, null, null);
+                 JFMImpl fmImpl = new JFMImpl(fm, javaBase, sink)) {
+                compiler.getTask(null, fmImpl, null, Arrays.asList("-proc:none"), null,
+                                 Arrays.asList(new SimpleJavaFileObject(new URI("mem:///module-info.java"), javax.tools.JavaFileObject.Kind.SOURCE) {
+                    @Override
+                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                        return moduleInfo.toString();
+                    }
+                })).call();
+            }
+
+            javaBase.refresh();
+            moduleBootOnJDK8 = ClassPathSupport.createClassPath(javaBase);
+        }
+
+        return moduleBootOnJDK8;
+    }
+
+    private static class JFMImpl extends ForwardingJavaFileManager<JavaFileManager> {
+        private final FileObject output;
+        private final FileObject sink;
+
+        public JFMImpl(JavaFileManager fileManager, FileObject output, FileObject sink) {
+            super(fileManager);
+            this.output = output;
+            this.sink = sink;
+        }
+
+        @Override
+        public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                List<JavaFileObject> result = new ArrayList<>();
+                FileObject pack = output.getFileObject(packageName.replace('.', '/'));
+
+                if (pack != null) {
+                    Enumeration<? extends FileObject> c = pack.getChildren(recurse);
+
+                    while (c.hasMoreElements()) {
+                        FileObject file = c.nextElement();
+                        if (!file.hasExt("class"))
+                            continue;
+                        result.add(new InferableJavaFileObject(file, JavaFileObject.Kind.CLASS));
+                    }
+                }
+
+                return result;
+            }
+            return super.list(location, packageName, kinds, recurse);
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, javax.tools.FileObject sibling) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                String relPath = className.replace('.', '/') + ".class";
+                try {
+                    return new SimpleJavaFileObject(new URI("mem://" + relPath), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return new ByteArrayOutputStream() {
+                                @Override
+                                public void close() throws IOException {
+                                    super.close();
+                                    FileObject target = FileUtil.createData(sink, relPath);
+                                    try (OutputStream out = target.getOutputStream()) {
+                                        out.write(toByteArray());
+                                    }
+                                }
+                            };
+                        }
+                    };
+                } catch (URISyntaxException ex) {
+                    throw new IOException(ex);
+                }
+            }
+            return super.getJavaFileForOutput(location, className, kind, sibling);
+        }
+
+        @Override
+        public String inferBinaryName(Location location, JavaFileObject file) {
+            if (file instanceof InferableJavaFileObject) {
+                return ((InferableJavaFileObject) file).className;
+            }
+            return super.inferBinaryName(location, file);
+        }
+
+        private class InferableJavaFileObject extends SimpleJavaFileObject {
+
+            private final String className;
+            public InferableJavaFileObject(FileObject file, Kind kind) {
+                super(file.toURI(), kind);
+                String relPath = FileUtil.getRelativePath(output, file);
+                className = relPath.substring(0, relPath.length() - ".class".length()).replace('/', '.');
+            }
+        }
+
+    }
+
+}
diff --git a/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java b/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java
index 202f548..98c80ce 100644
--- a/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java
+++ b/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java
@@ -56,13 +56,14 @@ import javax.swing.text.Document;
 import javax.tools.Diagnostic;
 import junit.framework.Assert;
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertFalse;
 
 import org.netbeans.api.editor.mimelookup.MimeLookup;
 import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
 import org.netbeans.api.java.lexer.JavaTokenId;
 import org.netbeans.api.java.queries.SourceForBinaryQuery;
 import org.netbeans.api.java.queries.SourceForBinaryQuery.Result;
@@ -97,8 +98,10 @@ import org.netbeans.modules.java.hints.spiimpl.SyntheticFix;
 import org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities;
 import org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker;
 import org.netbeans.modules.java.hints.spiimpl.options.HintsSettings;
+import org.netbeans.modules.java.hints.test.BootClassPathUtil;
 import org.netbeans.modules.java.hints.test.Utilities.TestLookup;
 import org.netbeans.modules.java.source.JavaSourceAccessor;
+import org.netbeans.modules.java.source.parsing.JavacParser;
 import org.netbeans.modules.parsing.api.indexing.IndexingManager;
 import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
 import org.netbeans.modules.parsing.impl.indexing.MimeTypes;
@@ -261,6 +264,7 @@ public class HintTest {
         sourcePath = ClassPathSupport.createClassPath(sourceRoot);
         
         Main.initializeURLFactory();
+        JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;
     }
 
     /**Bootstraps the test framework.
@@ -664,51 +668,18 @@ public class HintTest {
 
     }
 
-    private static List<URL> bootClassPath;
-
     private static Logger log = Logger.getLogger(HintTest.class.getName());
 
-    private static synchronized List<URL> getBootClassPath() {
-        if (bootClassPath == null) {
-            try {
-                String cp = System.getProperty("sun.boot.class.path");
-                List<URL> urls = new ArrayList<URL>();
-                String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
-
-                for (String path : paths) {
-                    File f = new File(path);
-
-                    if (!f.canRead())
-                        continue;
-
-                    FileObject fo = FileUtil.toFileObject(f);
-
-                    if (FileUtil.isArchiveFile(fo)) {
-                        fo = FileUtil.getArchiveRoot(fo);
-                    }
-
-                    if (fo != null) {
-                        urls.add(fo.getURL());
-                    }
-                }
-
-                bootClassPath = urls;
-            } catch (FileStateInvalidException e) {
-                if (log.isLoggable(Level.SEVERE))
-                    log.log(Level.SEVERE, e.getMessage(), e);
-            }
-        }
-
-        return bootClassPath;
-    }
-    
     private class TestProxyClassPathProvider implements ClassPathProvider {
 
         public ClassPath findClassPath(FileObject file, String type) {
             try {
             if (ClassPath.BOOT == type) {
-                // XXX simpler to use JavaPlatformManager.getDefault().getDefaultPlatform().getBootstrapLibraries()
-                return ClassPathSupport.createClassPath(getBootClassPath().toArray(new URL[0]));
+                return BootClassPathUtil.getBootClassPath();
+            }
+
+            if (JavaClassPathConstants.MODULE_BOOT_PATH == type) {
+                return BootClassPathUtil.getModuleBootPath();
             }
 
             if (ClassPath.SOURCE == type) {
diff --git a/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java b/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java
index a910d70..22a9970 100644
--- a/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java
+++ b/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java
@@ -302,4 +302,22 @@ public class HintTestTest {
         Assert.assertEquals("6\n7\n", doc.getText(0, doc.getLength()));
     }
 
+    @Test
+    public void testModuleBootPath() throws Exception {
+        HintTest.create()
+                .sourceLevel("9")
+                .input("module-info.java",
+                       "module m1 {}\n")
+                .run(ModuleBootPath.class)
+                .assertWarnings("0:0-0:12:verifier:Test");
+    }
+
+    @Hint(displayName="testModuleBootPath", description="testModuleBootPath", category="test")
+    public static final class ModuleBootPath {
+        @TriggerTreeKind(Kind.MODULE)
+        public static ErrorDescription hint(HintContext ctx) {
+            return ErrorDescriptionFactory.forTree(ctx, ctx.getPath(), "Test");
+        }
+    }
+
 }
diff --git a/java.hints/nbproject/project.xml b/java.hints/nbproject/project.xml
index 04e5500..a328b25 100644
--- a/java.hints/nbproject/project.xml
+++ b/java.hints/nbproject/project.xml
@@ -544,6 +544,9 @@
                         <compile-dependency/>
                     </test-dependency>
                     <test-dependency>
+                        <code-name-base>org.netbeans.modules.java.j2seplatform</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
                         <code-name-base>org.netbeans.modules.java.source.base</code-name-base>
                         <compile-dependency/>
                         <test/>
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java
new file mode 100644
index 0000000..7ea0fde
--- /dev/null
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 org.netbeans.modules.java.source;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.spi.java.classpath.PathResourceImplementation;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.BaseUtilities;
+
+import static junit.framework.TestCase.assertFalse;
+import org.openide.filesystems.MultiFileSystem;
+import org.openide.filesystems.Repository;
+
+/** Note this is duplicated in:
+ *  java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java
+ *
+ * @author lahvac
+ */
+public class BootClassPathUtil {
+
+    public static ClassPath getBootClassPath() {
+        String cp = System.getProperty("sun.boot.class.path");
+        if (cp != null) {
+            List<URL> urls = new ArrayList<>();
+            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
+            for (String path : paths) {
+                File f = new File(path);
+
+                if (!f.canRead())
+                    continue;
+
+                FileObject fo = FileUtil.toFileObject(f);
+                if (FileUtil.isArchiveFile(fo)) {
+                    fo = FileUtil.getArchiveRoot(fo);
+                }
+                if (fo != null) {
+                    urls.add(fo.toURL());
+                }
+            }
+            return ClassPathSupport.createClassPath((URL[])urls.toArray(new URL[0]));
+        } else {
+            try {
+                Class.forName("org.netbeans.ProxyURLStreamHandlerFactory").getMethod("register")
+                                                                          .invoke(null);
+            } catch (ClassNotFoundException | NoSuchMethodException |
+                     SecurityException | IllegalAccessException |
+                    IllegalArgumentException | InvocationTargetException ex) {
+                throw new IllegalStateException(ex);
+            }
+            final List<PathResourceImplementation> modules = new ArrayList<>();
+            final File installDir = new File(System.getProperty("java.home"));
+            final URI imageURI = getImageURI(installDir);
+            try {
+                final FileObject jrtRoot = URLMapper.findFileObject(imageURI.toURL());
+                final FileObject root = getModulesRoot(jrtRoot);
+                for (FileObject module : root.getChildren()) {
+                    modules.add(ClassPathSupport.createResource(module.toURL()));
+                }
+            } catch (MalformedURLException e) {
+                throw new IllegalStateException(e);
+            }
+            assertFalse(modules.isEmpty());
+            return ClassPathSupport.createClassPath(modules);
+        }
+    }
+
+    public static ClassPath getModuleBootPath() {
+        if (System.getProperty("sun.boot.class.path") != null) {
+            try {
+                //JDK 8:
+                return getModuleBootOnJDK8();
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            return getBootClassPath();
+        }
+    }
+
+    private static final String PROTOCOL = "nbjrt"; //NOI18N
+
+    private static URI getImageURI(@NonNull final File jdkHome) {
+        try {
+            return new URI(String.format(
+                "%s:%s!/%s",  //NOI18N
+                PROTOCOL,
+                BaseUtilities.toURI(jdkHome).toString(),
+                ""));
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @NonNull
+    private static FileObject getModulesRoot(@NonNull final FileObject jrtRoot) {
+        final FileObject modules = jrtRoot.getFileObject("modules");    //NOI18N
+        //jimage v1 - modules are located in the root
+        //jimage v2 - modules are located in "modules" folder
+        return modules == null ?
+            jrtRoot :
+            modules;
+    }
+
+    //create fake "system module path" while running on JDK 8
+    //java.base contains rt.jar and exports all java.* packages:
+    private static ClassPath moduleBootOnJDK8;
+
+    private static ClassPath getModuleBootOnJDK8() throws Exception {
+        if (moduleBootOnJDK8 == null) {
+            List<FileSystem> roots = new ArrayList<>();
+
+            FileSystem output = FileUtil.createMemoryFileSystem();
+            FileObject sink = output.getRoot();
+
+            roots.add(output);
+
+            Set<String> packages = new HashSet<>();
+            for (FileObject r : getBootClassPath().getRoots()) {
+                FileObject javaDir = r.getFileObject("java");
+
+                if (javaDir == null)
+                    continue;
+
+                roots.add(r.getFileSystem());
+
+                Enumeration<? extends FileObject> c = javaDir.getChildren(true);
+
+                while (c.hasMoreElements()) {
+                    FileObject current = c.nextElement();
+
+                    if (!current.isData() || !current.hasExt("class")) continue;
+
+                    String rel = FileUtil.getRelativePath(r, current.getParent());
+
+                    packages.add(rel.replace('/', '.'));
+                }
+            }
+
+            FileSystem outS = new MultiFileSystem(roots.toArray(new FileSystem[0])) {
+                {
+                    setSystemName("module-boot");
+                }
+            };
+
+            Repository.getDefault().addFileSystem(outS);
+
+            StringBuilder moduleInfo = new StringBuilder();
+
+            moduleInfo.append("module java.base {\n");
+
+            for (String pack : packages) {
+                moduleInfo.append("    exports " + pack + ";\n");
+            }
+
+            moduleInfo.append("}\n");
+
+            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+            FileObject javaBase = outS.getRoot();
+
+            try (JavaFileManager fm = compiler.getStandardFileManager(null, null, null);
+                 JFMImpl fmImpl = new JFMImpl(fm, javaBase, sink)) {
+                compiler.getTask(null, fmImpl, null, Arrays.asList("-proc:none"), null,
+                                 Arrays.asList(new SimpleJavaFileObject(new URI("mem:///module-info.java"), javax.tools.JavaFileObject.Kind.SOURCE) {
+                    @Override
+                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                        return moduleInfo.toString();
+                    }
+                })).call();
+            }
+
+            javaBase.refresh();
+            moduleBootOnJDK8 = ClassPathSupport.createClassPath(javaBase);
+        }
+
+        return moduleBootOnJDK8;
+    }
+
+    private static class JFMImpl extends ForwardingJavaFileManager<JavaFileManager> {
+        private final FileObject output;
+        private final FileObject sink;
+
+        public JFMImpl(JavaFileManager fileManager, FileObject output, FileObject sink) {
+            super(fileManager);
+            this.output = output;
+            this.sink = sink;
+        }
+
+        @Override
+        public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                List<JavaFileObject> result = new ArrayList<>();
+                FileObject pack = output.getFileObject(packageName.replace('.', '/'));
+
+                if (pack != null) {
+                    Enumeration<? extends FileObject> c = pack.getChildren(recurse);
+
+                    while (c.hasMoreElements()) {
+                        FileObject file = c.nextElement();
+                        if (!file.hasExt("class"))
+                            continue;
+                        result.add(new InferableJavaFileObject(file, JavaFileObject.Kind.CLASS));
+                    }
+                }
+
+                return result;
+            }
+            return super.list(location, packageName, kinds, recurse);
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, javax.tools.FileObject sibling) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                String relPath = className.replace('.', '/') + ".class";
+                try {
+                    return new SimpleJavaFileObject(new URI("mem://" + relPath), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return new ByteArrayOutputStream() {
+                                @Override
+                                public void close() throws IOException {
+                                    super.close();
+                                    FileObject target = FileUtil.createData(sink, relPath);
+                                    try (OutputStream out = target.getOutputStream()) {
+                                        out.write(toByteArray());
+                                    }
+                                }
+                            };
+                        }
+                    };
+                } catch (URISyntaxException ex) {
+                    throw new IOException(ex);
+                }
+            }
+            return super.getJavaFileForOutput(location, className, kind, sibling);
+        }
+
+        @Override
+        public String inferBinaryName(Location location, JavaFileObject file) {
+            if (file instanceof InferableJavaFileObject) {
+                return ((InferableJavaFileObject) file).className;
+            }
+            return super.inferBinaryName(location, file);
+        }
+
+        private class InferableJavaFileObject extends SimpleJavaFileObject {
+
+            private final String className;
+            public InferableJavaFileObject(FileObject file, Kind kind) {
+                super(file.toURI(), kind);
+                String relPath = FileUtil.getRelativePath(output, file);
+                className = relPath.substring(0, relPath.length() - ".class".length()).replace('/', '.');
+            }
+        }
+
+    }
+
+}
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java
index cf658e7..8245627 100644
--- a/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java
@@ -405,65 +405,7 @@ public class TestUtil {
       }
 
     public static ClassPath getBootClassPath() {
-        String cp = System.getProperty("sun.boot.class.path");
-        if (cp != null) {
-            List<URL> urls = new ArrayList<>();
-            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
-            for (String path : paths) {
-                File f = new File(path);
-
-                if (!f.canRead())
-                    continue;
-
-                FileObject fo = FileUtil.toFileObject(f);
-                if (FileUtil.isArchiveFile(fo)) {
-                    fo = FileUtil.getArchiveRoot(fo);
-                }
-                if (fo != null) {
-                    urls.add(fo.toURL());
-                }
-            }
-            return ClassPathSupport.createClassPath((URL[])urls.toArray(new URL[0]));
-        } else {
-            ProxyURLStreamHandlerFactory.register();
-            final List<PathResourceImplementation> modules = new ArrayList<>();
-            final File installDir = new File(System.getProperty("java.home"));
-            final URI imageURI = getImageURI(installDir);
-            try {
-                final FileObject jrtRoot = URLMapper.findFileObject(imageURI.toURL());
-                final FileObject root = getModulesRoot(jrtRoot);
-                for (FileObject module : root.getChildren()) {
-                    modules.add(ClassPathSupport.createResource(module.toURL()));
-                }
-            } catch (MalformedURLException e) {
-                throw new IllegalStateException(e);
-            }
-            assertFalse(modules.isEmpty());
-            return ClassPathSupport.createClassPath(modules);
-        }
+        return BootClassPathUtil.getBootClassPath();
     }
 
-    private static final String PROTOCOL = "nbjrt"; //NOI18N
-
-    private static URI getImageURI(@NonNull final File jdkHome) {
-        try {
-            return new URI(String.format(
-                "%s:%s!/%s",  //NOI18N
-                PROTOCOL,
-                BaseUtilities.toURI(jdkHome).toString(),
-                ""));
-        } catch (URISyntaxException e) {
-            throw new IllegalStateException();
-        }
-    }
-
-    @NonNull
-    private static FileObject getModulesRoot(@NonNull final FileObject jrtRoot) {
-        final FileObject modules = jrtRoot.getFileObject("modules");    //NOI18N
-        //jimage v1 - modules are located in the root
-        //jimage v2 - modules are located in "modules" folder
-        return modules == null ?
-            jrtRoot :
-            modules;
-    }
 }

-- 
To stop receiving notification emails like this one, please contact
geertjan@apache.org.

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists