1 /*
  2  * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @summary Basic test for Enable-Native-Access attribute in the
 27  *          manifest of a main application JAR
 28  * @library /test/lib
 29  * @requires jdk.foreign.linker != "UNSUPPORTED"
 30  * @requires !vm.musl
 31  * @comment Disable test for the zero VM config due to 8380059.
 32  * @requires vm.flavor != "zero"
 33  *
 34  * @enablePreview
 35  * @build TestEnableNativeAccessJarManifest
 36  *        panama_module/*
 37  *        org.openjdk.foreigntest.unnamed.PanamaMainUnnamedModule
 38  * @run testng/native TestEnableNativeAccessJarManifest
 39  */
 40 
 41 import java.nio.file.Files;
 42 import java.nio.file.Path;
 43 import java.nio.file.Paths;
 44 import java.util.List;
 45 import java.util.ArrayList;
 46 import java.util.jar.Attributes;
 47 import java.util.jar.Manifest;
 48 
 49 import jdk.test.lib.process.OutputAnalyzer;
 50 import jdk.test.lib.process.ProcessTools;
 51 import jdk.test.lib.util.JarUtils;
 52 
 53 import org.testng.annotations.Test;
 54 import org.testng.annotations.DataProvider;
 55 
 56 public class TestEnableNativeAccessJarManifest extends TestEnableNativeAccessBase {
 57 
 58     private static final String REINVOKER = "TestEnableNativeAccessJarManifest$Reinvoker";
 59 
 60     static record Attribute(String name, String value) {}
 61 
 62     @Test(dataProvider = "cases")
 63     public void testEnableNativeAccessInJarManifest(String action, String cls, Result expectedResult,
 64                                                     List<Attribute> attributes, List<String> vmArgs, List<String> programArgs) throws Exception {
 65         Manifest man = new Manifest();
 66         Attributes attrs = man.getMainAttributes();
 67         attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0");
 68         attrs.put(Attributes.Name.MAIN_CLASS, cls);
 69 
 70         for (Attribute attrib : attributes) {
 71             attrs.put(new Attributes.Name(attrib.name()), attrib.value());
 72         }
 73 
 74         // create the JAR file with Test1 and Test2
 75         Path jarfile = Paths.get(action + ".jar");
 76         Files.deleteIfExists(jarfile);
 77 
 78         Path classes = Paths.get(System.getProperty("test.classes", ""));
 79         JarUtils.createJarFile(jarfile, man, classes, Paths.get(cls.replace('.', '/') + ".class"));
 80 
 81         // java -jar test.jar
 82         List<String> command = new ArrayList<>(List.of(
 83             "-Djava.library.path=" + System.getProperty("java.library.path")
 84         ));
 85         command.addAll(vmArgs);
 86         command.add("-jar");
 87         command.add(jarfile.toString());
 88         command.addAll(programArgs);
 89         OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(command.toArray(String[]::new))
 90                 .outputTo(System.out)
 91                 .errorTo(System.out);
 92         checkResult(expectedResult, outputAnalyzer);
 93     }
 94 
 95     @DataProvider
 96     public Object[][] cases() {
 97         return new Object[][] {
 98             // simple cases where a jar contains a single main class with no dependencies
 99             { "panama_no_unnamed_module_native_access", UNNAMED, successWithWarning("ALL-UNNAMED"),
100                     List.of(), List.of(), List.of() },
101             { "panama_unnamed_module_native_access", UNNAMED, successNoWarning(),
102                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")), List.of(), List.of() },
103             { "panama_unnamed_module_native_access_invalid", UNNAMED,
104                     failWithError("Error: illegal value \"asdf\" for Enable-Native-Access manifest attribute. Only ALL-UNNAMED is allowed"),
105                     List.of(new Attribute("Enable-Native-Access", "asdf")), List.of(), List.of() },
106 
107             // more complex cases where a jar invokes a module on the module path that does native access
108             { "panama_enable_native_access_false", REINVOKER, successWithWarning("panama_module"),
109                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")),
110                     List.of("-p", MODULE_PATH, "--add-modules=panama_module"),
111                     List.of(PANAMA_MAIN_CLS) },
112             { "panama_enable_native_access_reflection_false", REINVOKER, successWithWarning("panama_module"),
113                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")),
114                     List.of("-p", MODULE_PATH, "--add-modules=panama_module"),
115                     List.of(PANAMA_REFLECTION_CLS) },
116             { "panama_enable_native_access_invoke_false", REINVOKER, successWithWarning("panama_module"),
117                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")),
118                     List.of("-p", MODULE_PATH, "--add-modules=panama_module"),
119                     List.of(PANAMA_INVOKE_CLS) },
120 
121             { "panama_enable_native_access_true", REINVOKER, successNoWarning(),
122                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")),
123                     List.of("-p", MODULE_PATH, "--add-modules=panama_module", "--enable-native-access=panama_module"),
124                     List.of(PANAMA_MAIN_CLS) },
125             { "panama_enable_native_access_reflection_true", REINVOKER, successNoWarning(),
126                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")),
127                     List.of("-p", MODULE_PATH, "--add-modules=panama_module", "--enable-native-access=panama_module"),
128                     List.of(PANAMA_REFLECTION_CLS) },
129             { "panama_enable_native_access_invoke_true", REINVOKER, successNoWarning(),
130                     List.of(new Attribute("Enable-Native-Access", "ALL-UNNAMED")),
131                     List.of("-p", MODULE_PATH, "--add-modules=panama_module", "--enable-native-access=panama_module"),
132                     List.of(PANAMA_INVOKE_CLS) }
133         };
134     }
135 
136     public class Reinvoker {
137         public static void main(String[] args) throws Throwable {
138             Class<?> realMainClass = Class.forName(args[0]);
139             realMainClass.getMethod("main", String[].class).invoke(null, (Object) new String[0]);
140         }
141     }
142 }