1/*
2 * Copyright (c) 2016, 2017, 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#include <stdio.h>
25#include <string.h>
26#include "jvmti.h"
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32#ifndef JNI_ENV_ARG
33
34#ifdef __cplusplus
35#define JNI_ENV_ARG(x, y) y
36#define JNI_ENV_PTR(x) x
37#else
38#define JNI_ENV_ARG(x,y) x, y
39#define JNI_ENV_PTR(x) (*x)
40#endif
41
42#endif
43
44#define TranslateError(err) "JVMTI error"
45
46#define PASSED 0
47#define FAILED 2
48
49static const char *EXC_CNAME = "java/lang/Exception";
50static const char* MOD_CNAME = "Ljava/lang/Module;";
51
52static jvmtiEnv *jvmti = NULL;
53static jint result = PASSED;
54
55static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
56
57JNIEXPORT
58jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
59    return Agent_Initialize(jvm, options, reserved);
60}
61
62JNIEXPORT
63jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
64    return Agent_Initialize(jvm, options, reserved);
65}
66
67JNIEXPORT
68jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
69    return JNI_VERSION_1_8;
70}
71
72static
73jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
74    jint res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
75                                        JVMTI_VERSION_9);
76    if (res != JNI_OK || jvmti == NULL) {
77        printf("    Error: wrong result of a valid call to GetEnv!\n");
78        return JNI_ERR;
79    }
80
81    return JNI_OK;
82}
83
84static
85void throw_exc(JNIEnv *env, char *msg) {
86    jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME));
87    jint rt = JNI_OK;
88
89    if (exc_class == NULL) {
90        printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME);
91        return;
92    }
93    rt = JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
94    if (rt == JNI_ERR) {
95        printf("throw_exc: Error in JNI ThrowNew(env, %s)\n", msg);
96    }
97}
98
99static
100jclass jlM(JNIEnv *env) {
101    jclass cls = NULL;
102
103    cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, MOD_CNAME));
104    if (cls == NULL) {
105        printf("    Error in JNI FindClass: %s\n", MOD_CNAME);
106    }
107    return cls;
108}
109
110jmethodID
111get_method(JNIEnv *env, jclass clazz, const char * name, const char *sig) {
112    jmethodID method = NULL;
113
114    method = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG(env, clazz), name, sig);
115    if (method == NULL) {
116        printf("    Error in JNI GetMethodID %s with signature %s", name, sig);
117    }
118    return method;
119}
120
121static
122jboolean is_exported(JNIEnv *env, jobject module, const char* pkg, jboolean open) {
123    static jmethodID mIsExported = NULL;
124    jstring jstr = NULL;
125    jboolean res = JNI_FALSE;
126
127    if (mIsExported == NULL) {
128        const char* sign = "(Ljava/lang/String;)Z";
129        const char* name = open ? "isOpen" : "isExported";
130        mIsExported = get_method(env, jlM(env), name, sign);
131    }
132    jstr = JNI_ENV_PTR(env)->NewStringUTF(JNI_ENV_ARG(env, pkg));
133    res = JNI_ENV_PTR(env)->CallBooleanMethod(JNI_ENV_ARG(env, module),
134                                              mIsExported, jstr);
135    return res;
136}
137
138static
139jboolean is_exported_to(JNIEnv *env, jobject module, const char* pkg, jobject to_module,
140                        jboolean open) {
141    static jmethodID mIsExportedTo = NULL;
142    jstring jstr = NULL;
143    jboolean res = JNI_FALSE;
144
145    if (mIsExportedTo == NULL) {
146        const char* sign = "(Ljava/lang/String;Ljava/lang/Module;)Z";
147        const char* name = open ? "isOpen" : "isExported";
148        mIsExportedTo = get_method(env, jlM(env), name, sign);
149    }
150    jstr = JNI_ENV_PTR(env)->NewStringUTF(JNI_ENV_ARG(env, pkg));
151    res = JNI_ENV_PTR(env)->CallBooleanMethod(JNI_ENV_ARG(env, module),
152                                              mIsExportedTo, jstr, to_module);
153    return res;
154}
155
156static
157jvmtiError add_module_exports(jobject baseModule, const char* pkg, jobject thisModule,
158                              jboolean open) {
159    jvmtiError err = JVMTI_ERROR_NONE;
160    if (open) {
161        err = (*jvmti)->AddModuleOpens(jvmti, baseModule, pkg, thisModule);
162    } else {
163        err = (*jvmti)->AddModuleExports(jvmti, baseModule, pkg, thisModule);
164    }
165    return err;
166}
167
168static
169jint check_add_module_exports(JNIEnv *env,
170                              jclass  cls,
171                              jobject baseModule,
172                              jobject thisModule,
173                              jboolean open) {
174    static char strbuf[128] = { '\0' };
175    jvmtiError err = JVMTI_ERROR_NONE;
176    const char* pkg = open ? "jdk.internal.math"
177                           : "jdk.internal.misc";
178    const char* bad_pkg = "my.bad.pkg";
179    const char* jvmti_fn = open ? "AddModuleOpens"
180                                : "AddModuleExports";
181    jboolean exported = JNI_FALSE;
182
183    // Export from NULL module
184    printf("Check #N1:\n");
185    err = add_module_exports(NULL, pkg, thisModule, open);
186    if (err != JVMTI_ERROR_NULL_POINTER) {
187        printf("#N1: jvmtiError from %s: %d\n", jvmti_fn, err);
188        throw_exc(env, "Check #N1: failed to return JVMTI_ERROR_NULL_POINTER for module==NULL");
189        return FAILED;
190    }
191
192    // Export NULL package
193    printf("Check #N2:\n");
194    err = add_module_exports(baseModule, NULL, thisModule, open);
195    if (err != JVMTI_ERROR_NULL_POINTER) {
196        printf("#N2: jvmtiError from %s: %d\n", jvmti_fn, err);
197        throw_exc(env, "Check #N2: failed to return JVMTI_ERROR_NULL_POINTER for pkg==NULL");
198        return FAILED;
199    }
200
201    // Export to NULL module
202    printf("Check #N3:\n");
203    err = add_module_exports(baseModule, pkg, NULL, open);
204    if (err != JVMTI_ERROR_NULL_POINTER) {
205        printf("#N3: jvmtiError from %s: %d\n", jvmti_fn, err);
206        throw_exc(env, "Check #N3: failed to return JVMTI_ERROR_NULL_POINTER for to_module==NULL");
207        return FAILED;
208    }
209
210    // Export a bad package
211    printf("Check #I0:\n");
212    err = add_module_exports(baseModule, bad_pkg, thisModule, open);
213    if (err != JVMTI_ERROR_ILLEGAL_ARGUMENT) {
214        printf("#I0: jvmtiError from %s: %d\n", jvmti_fn, err);
215        throw_exc(env, "Check #I0: did not get expected JVMTI_ERROR_ILLEGAL_ARGUMENT for invalid package");
216        return FAILED;
217    }
218
219    // Export from invalid module (cls)
220    printf("Check #I1:\n");
221    err = add_module_exports((jobject)cls, pkg, thisModule, open);
222    if (err != JVMTI_ERROR_INVALID_MODULE) {
223        printf("#I1: jvmtiError from %s: %d\n", jvmti_fn, err);
224        throw_exc(env, "Check #I1: did not get expected JVMTI_ERROR_INVALID_MODULE for invalid module");
225        return FAILED;
226    }
227
228    // Export to invalid module (cls)
229    printf("Check #I2:\n");
230    err = add_module_exports(baseModule, pkg, (jobject)cls, open);
231    if (err != JVMTI_ERROR_INVALID_MODULE) {
232        printf("#I2: jvmtiError from %s: %d\n", jvmti_fn, err);
233        throw_exc(env, "Check #I2: did not get expected JVMTI_ERROR_INVALID_MODULE for invalid to_module");
234        return FAILED;
235    }
236
237    // Check the pkg is not exported from baseModule to thisModule
238    printf("Check #C0:\n");
239    exported = is_exported_to(env, baseModule, pkg, thisModule, open);
240    if (exported != JNI_FALSE) {
241        sprintf(strbuf, "Check #C0: unexpected export of %s from base to this", pkg);
242        throw_exc(env, strbuf);
243        return FAILED;
244    }
245
246    // Add export of the pkg from baseModule to thisModule
247    printf("Check #C1:\n");
248    err = add_module_exports(baseModule, pkg, thisModule, open);
249    if (err != JVMTI_ERROR_NONE) {
250        printf("#C1: jvmtiError from %s: %d\n", jvmti_fn, err);
251        sprintf(strbuf, "Check #C1: error in add export of %s from base to this", pkg);
252        throw_exc(env, strbuf);
253        return FAILED;
254    }
255
256    // Check the pkg is exported from baseModule to thisModule
257    printf("Check #C2:\n");
258    exported = is_exported_to(env, baseModule, pkg, thisModule, open);
259    if (exported == JNI_FALSE) {
260        sprintf(strbuf, "Check #C2: failed to export %s from base to this", pkg);
261        throw_exc(env, strbuf);
262        return FAILED;
263    }
264
265    // Check the pkg is not exported to all modules
266    printf("Check #C3:\n");
267    exported = is_exported(env, baseModule, pkg, open);
268    if (exported != JNI_FALSE) {
269        sprintf(strbuf, "Check #C3: unexpected export of %s from base to all modules", pkg);
270        throw_exc(env, strbuf);
271        return FAILED;
272    }
273    return PASSED;
274}
275
276JNIEXPORT jint JNICALL
277Java_MyPackage_AddModuleExportsAndOpensTest_check(JNIEnv *env,
278                                                  jclass cls,
279                                                  jobject baseModule,
280                                                  jobject thisModule) {
281    if (jvmti == NULL) {
282        throw_exc(env, "JVMTI client was not properly loaded!\n");
283        return FAILED;
284    }
285
286    printf("\n*** Checks for JVMTI AddModuleExports ***\n\n");
287    result = check_add_module_exports(env, cls, baseModule, thisModule, JNI_FALSE);
288    if (result != PASSED) {
289        return result;
290    }
291
292    printf("\n*** Checks for JVMTI AddModuleOpens ***\n\n");
293    result = check_add_module_exports(env, cls, baseModule, thisModule, JNI_TRUE);
294    if (result != PASSED) {
295        return result;
296    }
297    return result;
298}
299
300#ifdef __cplusplus
301}
302#endif
303