changeset 12800:032a5041e20a

Merge
author alanb
date Sat, 17 Jun 2017 08:03:09 +0100
parents 409ae96e9140 (diff) 0d4a6056e3cc (current diff)
children 8346c00b2ba6
files test/runtime/modules/JVMAddModulePackage.java
diffstat 15 files changed, 392 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/make/test/JtregNative.gmk	Fri Jun 16 09:20:23 2017 -0700
+++ b/make/test/JtregNative.gmk	Sat Jun 17 08:03:09 2017 +0100
@@ -56,6 +56,7 @@
     $(HOTSPOT_TOPDIR)/test/compiler/floatingpoint/ \
     $(HOTSPOT_TOPDIR)/test/compiler/calls \
     $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/GetNamedModule \
+    $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/IsModifiableModule \
     $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/AddModuleReads \
     $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/AddModuleExportsAndOpens \
     $(HOTSPOT_TOPDIR)/test/serviceability/jvmti/AddModuleUsesAndProvides \
@@ -85,6 +86,7 @@
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_liboverflow := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libSimpleClassFileLoadHook := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libGetNamedModuleTest := -lc
+    BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libIsModifiableModuleTest := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAddModuleReadsTest := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAddModuleExportsAndOpensTest := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAddModuleUsesAndProvidesTest := -lc
--- a/src/cpu/aarch64/vm/vtableStubs_aarch64.cpp	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/cpu/aarch64/vm/vtableStubs_aarch64.cpp	Sat Jun 17 08:03:09 2017 +0100
@@ -51,6 +51,11 @@
 VtableStub* VtableStubs::create_vtable_stub(int vtable_index) {
   const int aarch64_code_length = VtableStub::pd_code_size_limit(true);
   VtableStub* s = new(aarch64_code_length) VtableStub(true, vtable_index);
+  // Can be NULL if there is no free space in the code cache.
+  if (s == NULL) {
+    return NULL;
+  }
+
   ResourceMark rm;
   CodeBuffer cb(s->entry_point(), aarch64_code_length);
   MacroAssembler* masm = new MacroAssembler(&cb);
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	Sat Jun 17 08:03:09 2017 +0100
@@ -644,4 +644,9 @@
      *         {@link Long}
      */
     native Object getFlagValue(String name);
+
+    /**
+     * Gets the host class for {@code type}.
+     */
+    native HotSpotResolvedObjectTypeImpl getHostClass(HotSpotResolvedObjectTypeImpl type);
 }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java	Sat Jun 17 08:03:09 2017 +0100
@@ -412,6 +412,14 @@
     }
 
     @Override
+    public ResolvedJavaType getHostClass() {
+        if (isArray()) {
+            return null;
+        }
+        return compilerToVM().getHostClass(this);
+    }
+
+    @Override
     public boolean isJavaLangObject() {
         return javaClass.equals(Object.class);
     }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java	Sat Jun 17 08:03:09 2017 +0100
@@ -154,6 +154,11 @@
     }
 
     @Override
+    public ResolvedJavaType getHostClass() {
+        return null;
+    }
+
+    @Override
     public JavaKind getJavaKind() {
         return kind;
     }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ResolvedJavaType.java	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/ResolvedJavaType.java	Sat Jun 17 08:03:09 2017 +0100
@@ -105,6 +105,13 @@
     boolean isAssignableFrom(ResolvedJavaType other);
 
     /**
+     * Returns the {@link ResolvedJavaType} object representing the host class of this VM anonymous
+     * class (as opposed to the unrelated concept specified by {@link Class#isAnonymousClass()}) or
+     * {@code null} if this object does not represent a VM anonymous class.
+     */
+    ResolvedJavaType getHostClass();
+
+    /**
      * Returns true if this type is exactly the type {@link java.lang.Object}.
      */
     default boolean isJavaLangObject() {
--- a/src/share/vm/code/codeCache.cpp	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/share/vm/code/codeCache.cpp	Sat Jun 17 08:03:09 2017 +0100
@@ -130,6 +130,7 @@
 // Iterate over all CodeHeaps
 #define FOR_ALL_HEAPS(heap) for (GrowableArrayIterator<CodeHeap*> heap = _heaps->begin(); heap != _heaps->end(); ++heap)
 #define FOR_ALL_NMETHOD_HEAPS(heap) for (GrowableArrayIterator<CodeHeap*> heap = _nmethod_heaps->begin(); heap != _nmethod_heaps->end(); ++heap)
+#define FOR_ALL_ALLOCABLE_HEAPS(heap) for (GrowableArrayIterator<CodeHeap*> heap = _allocable_heaps->begin(); heap != _allocable_heaps->end(); ++heap)
 
 // Iterate over all CodeBlobs (cb) on the given CodeHeap
 #define FOR_ALL_BLOBS(cb, heap) for (CodeBlob* cb = first_blob(heap); cb != NULL; cb = next_blob(heap, cb))
@@ -140,10 +141,11 @@
 bool CodeCache::_needs_cache_clean = false;
 nmethod* CodeCache::_scavenge_root_nmethods = NULL;
 
-// Initialize array of CodeHeaps
+// Initialize arrays of CodeHeap subsets
 GrowableArray<CodeHeap*>* CodeCache::_heaps = new(ResourceObj::C_HEAP, mtCode) GrowableArray<CodeHeap*> (CodeBlobType::All, true);
 GrowableArray<CodeHeap*>* CodeCache::_compiled_heaps = new(ResourceObj::C_HEAP, mtCode) GrowableArray<CodeHeap*> (CodeBlobType::All, true);
 GrowableArray<CodeHeap*>* CodeCache::_nmethod_heaps = new(ResourceObj::C_HEAP, mtCode) GrowableArray<CodeHeap*> (CodeBlobType::All, true);
+GrowableArray<CodeHeap*>* CodeCache::_allocable_heaps = new(ResourceObj::C_HEAP, mtCode) GrowableArray<CodeHeap*> (CodeBlobType::All, true);
 
 void CodeCache::check_heap_sizes(size_t non_nmethod_size, size_t profiled_size, size_t non_profiled_size, size_t cache_size, bool all_set) {
   size_t total_size = non_nmethod_size + profiled_size + non_profiled_size;
@@ -338,6 +340,7 @@
   return rs;
 }
 
+// Heaps available for allocation
 bool CodeCache::heap_available(int code_blob_type) {
   if (!SegmentedCodeCache) {
     // No segmentation: use a single code heap
@@ -391,6 +394,9 @@
   if (code_blob_type_accepts_nmethod(type)) {
     _nmethod_heaps->insert_sorted<code_heap_compare>(heap);
   }
+  if (code_blob_type_accepts_allocable(type)) {
+    _allocable_heaps->insert_sorted<code_heap_compare>(heap);
+  }
 }
 
 void CodeCache::add_heap(ReservedSpace rs, const char* name, int code_blob_type) {
@@ -620,7 +626,7 @@
 
 void CodeCache::blobs_do(void f(CodeBlob* nm)) {
   assert_locked_or_safepoint(CodeCache_lock);
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_HEAPS(heap) {
     FOR_ALL_BLOBS(cb, *heap) {
       f(cb);
     }
@@ -663,7 +669,7 @@
 
 void CodeCache::blobs_do(CodeBlobClosure* f) {
   assert_locked_or_safepoint(CodeCache_lock);
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     FOR_ALL_BLOBS(cb, *heap) {
       if (cb->is_alive()) {
         f->do_code_blob(cb);
@@ -960,7 +966,7 @@
 
 size_t CodeCache::capacity() {
   size_t cap = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     cap += (*heap)->capacity();
   }
   return cap;
@@ -973,7 +979,7 @@
 
 size_t CodeCache::unallocated_capacity() {
   size_t unallocated_cap = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     unallocated_cap += (*heap)->unallocated_capacity();
   }
   return unallocated_cap;
@@ -981,7 +987,7 @@
 
 size_t CodeCache::max_capacity() {
   size_t max_cap = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     max_cap += (*heap)->max_capacity();
   }
   return max_cap;
@@ -1007,7 +1013,7 @@
 
 size_t CodeCache::bytes_allocated_in_freelists() {
   size_t allocated_bytes = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     allocated_bytes += (*heap)->allocated_in_freelist();
   }
   return allocated_bytes;
@@ -1015,7 +1021,7 @@
 
 int CodeCache::allocated_segments() {
   int number_of_segments = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     number_of_segments += (*heap)->allocated_segments();
   }
   return number_of_segments;
@@ -1023,7 +1029,7 @@
 
 size_t CodeCache::freelists_length() {
   size_t length = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     length += (*heap)->freelist_length();
   }
   return length;
@@ -1354,7 +1360,7 @@
 
 void CodeCache::print_memory_overhead() {
   size_t wasted_bytes = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
       CodeHeap* curr_heap = *heap;
       for (CodeBlob* cb = (CodeBlob*)curr_heap->first(); cb != NULL; cb = (CodeBlob*)curr_heap->next(cb)) {
         HeapBlock* heap_block = ((HeapBlock*)cb) - 1;
@@ -1400,7 +1406,7 @@
   ResourceMark rm;
 
   int i = 0;
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     if ((_nmethod_heaps->length() >= 1) && Verbose) {
       tty->print_cr("-- %s --", (*heap)->name());
     }
@@ -1497,7 +1503,7 @@
   CodeBlob_sizes live;
   CodeBlob_sizes dead;
 
-  FOR_ALL_NMETHOD_HEAPS(heap) {
+  FOR_ALL_ALLOCABLE_HEAPS(heap) {
     FOR_ALL_BLOBS(cb, *heap) {
       if (!cb->is_alive()) {
         dead.add(cb);
@@ -1523,7 +1529,7 @@
     int number_of_blobs = 0;
     int number_of_oop_maps = 0;
     int map_size = 0;
-    FOR_ALL_NMETHOD_HEAPS(heap) {
+    FOR_ALL_ALLOCABLE_HEAPS(heap) {
       FOR_ALL_BLOBS(cb, *heap) {
         if (cb->is_alive()) {
           number_of_blobs++;
--- a/src/share/vm/code/codeCache.hpp	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/share/vm/code/codeCache.hpp	Sat Jun 17 08:03:09 2017 +0100
@@ -85,6 +85,7 @@
   static GrowableArray<CodeHeap*>* _heaps;
   static GrowableArray<CodeHeap*>* _compiled_heaps;
   static GrowableArray<CodeHeap*>* _nmethod_heaps;
+  static GrowableArray<CodeHeap*>* _allocable_heaps;
 
   static address _low_bound;                            // Lower bound of CodeHeap addresses
   static address _high_bound;                           // Upper bound of CodeHeap addresses
@@ -237,6 +238,11 @@
     return type == CodeBlobType::All || type <= CodeBlobType::MethodProfiled;
   }
 
+  static bool code_blob_type_accepts_allocable(int type) {
+    return type <= CodeBlobType::All;
+  }
+
+
   // Returns the CodeBlobType for the given compilation level
   static int get_code_blob_type(int comp_level) {
     if (comp_level == CompLevel_none ||
--- a/src/share/vm/compiler/compileBroker.cpp	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/share/vm/compiler/compileBroker.cpp	Sat Jun 17 08:03:09 2017 +0100
@@ -1557,7 +1557,7 @@
 
   // First thread to get here will initialize the compiler interface
 
-  if (!ciObjectFactory::is_initialized()) {
+  {
     ASSERT_IN_VM;
     MutexLocker only_one (CompileThread_lock, thread);
     if (!ciObjectFactory::is_initialized()) {
--- a/src/share/vm/jvmci/jvmciCompilerToVM.cpp	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/share/vm/jvmci/jvmciCompilerToVM.cpp	Sat Jun 17 08:03:09 2017 +0100
@@ -1687,6 +1687,13 @@
   }
 C2V_END
 
+C2V_VMENTRY(jobject, getHostClass, (JNIEnv*, jobject, jobject jvmci_type))
+  InstanceKlass* k = InstanceKlass::cast(CompilerToVM::asKlass(jvmci_type));
+  InstanceKlass* host = k->host_klass();
+  oop result = CompilerToVM::get_jvmci_type(host, CHECK_NULL);
+  return JNIHandles::make_local(THREAD, result);
+C2V_END
+
 C2V_VMENTRY(int, interpreterFrameSize, (JNIEnv*, jobject, jobject bytecode_frame_handle))
   if (bytecode_frame_handle == NULL) {
     THROW_0(vmSymbols::java_lang_NullPointerException());
@@ -1817,6 +1824,7 @@
   {CC "flushDebugOutput",                             CC "()V",                                                                             FN_PTR(flushDebugOutput)},
   {CC "methodDataProfileDataSize",                    CC "(JI)I",                                                                           FN_PTR(methodDataProfileDataSize)},
   {CC "getFingerprint",                               CC "(J)J",                                                                            FN_PTR(getFingerprint)},
+  {CC "getHostClass",                                 CC "(" HS_RESOLVED_KLASS ")" HS_RESOLVED_KLASS,                                       FN_PTR(getHostClass)},
   {CC "interpreterFrameSize",                         CC "(" BYTECODE_FRAME ")I",                                                           FN_PTR(interpreterFrameSize)},
   {CC "compileToBytecode",                            CC "(" OBJECT ")V",                                                                   FN_PTR(compileToBytecode)},
   {CC "getFlagValue",                                 CC "(" STRING ")" OBJECT,                                                             FN_PTR(getFlagValue)},
--- a/src/share/vm/services/lowMemoryDetector.cpp	Fri Jun 16 09:20:23 2017 -0700
+++ b/src/share/vm/services/lowMemoryDetector.cpp	Sat Jun 17 08:03:09 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -296,19 +296,41 @@
     Klass* k = Management::sun_management_Sensor_klass(CHECK);
     instanceKlassHandle sensorKlass (THREAD, k);
     Handle sensor_h(THREAD, _sensor_obj);
-    Handle usage_h = MemoryService::create_MemoryUsage_obj(_usage, CHECK);
+
+    Symbol* trigger_method_signature;
 
     JavaValue result(T_VOID);
     JavaCallArguments args(sensor_h);
     args.push_int((int) count);
-    args.push_oop(usage_h);
+
+    Handle usage_h = MemoryService::create_MemoryUsage_obj(_usage, THREAD);
+    // Call Sensor::trigger(int, MemoryUsage) to send notification to listeners.
+    // When OOME occurs and fails to allocate MemoryUsage object, call
+    // Sensor::trigger(int) instead.  The pending request will be processed
+    // but no notification will be sent.
+    if (HAS_PENDING_EXCEPTION) {
+       assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOME here");
+       CLEAR_PENDING_EXCEPTION;
+       trigger_method_signature = vmSymbols::int_void_signature();
+    } else {
+       trigger_method_signature = vmSymbols::trigger_method_signature();
+       args.push_oop(usage_h);
+    }
 
     JavaCalls::call_virtual(&result,
-                            sensorKlass,
-                            vmSymbols::trigger_name(),
-                            vmSymbols::trigger_method_signature(),
-                            &args,
-                            CHECK);
+                        sensorKlass,
+                        vmSymbols::trigger_name(),
+                        trigger_method_signature,
+                        &args,
+                        THREAD);
+
+    if (HAS_PENDING_EXCEPTION) {
+       // We just clear the OOM pending exception that we might have encountered
+       // in Java's tiggerAction(), and continue with updating the counters since
+       // the Java counters have been updated too.
+       assert((PENDING_EXCEPTION->is_a(SystemDictionary::OutOfMemoryError_klass())), "we expect only an OOME here");
+       CLEAR_PENDING_EXCEPTION;
+     }
   }
 
   {
--- a/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java	Fri Jun 16 09:20:23 2017 -0700
+++ b/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java	Sat Jun 17 08:03:09 2017 +0100
@@ -54,6 +54,7 @@
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.function.Supplier;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -143,6 +144,27 @@
     }
 
     @Test
+    public void getHostClassTest() {
+        for (Class<?> c : classes) {
+            ResolvedJavaType type = metaAccess.lookupJavaType(c);
+            ResolvedJavaType host = type.getHostClass();
+            assertNull(host);
+        }
+
+        class LocalClass {}
+        Cloneable clone = new Cloneable() {};
+        assertNull(metaAccess.lookupJavaType(LocalClass.class).getHostClass());
+        assertNull(metaAccess.lookupJavaType(clone.getClass()).getHostClass());
+
+        Supplier<Runnable> lambda = () -> () -> System.out.println("run");
+        ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass());
+        ResolvedJavaType nestedLambdaType = metaAccess.lookupJavaType(lambda.get().getClass());
+        assertNotNull(lambdaType.getHostClass());
+        assertNotNull(nestedLambdaType.getHostClass());
+        assertEquals(lambdaType.getHostClass(), nestedLambdaType.getHostClass());
+    }
+
+    @Test
     public void getModifiersTest() {
         for (Class<?> c : classes) {
             ResolvedJavaType type = metaAccess.lookupJavaType(c);
--- a/test/serviceability/jvmti/GetNamedModule/MyPackage/GetNamedModuleTest.java	Fri Jun 16 09:20:23 2017 -0700
+++ b/test/serviceability/jvmti/GetNamedModule/MyPackage/GetNamedModuleTest.java	Sat Jun 17 08:03:09 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 /**
  * @test
  * @summary Verifies the JVMTI GetNamedModule API
+ * @modules jdk.jdi
  * @compile GetNamedModuleTest.java
  * @run main/othervm/native -agentlib:GetNamedModuleTest MyPackage.GetNamedModuleTest
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/serviceability/jvmti/IsModifiableModule/MyPackage/IsModifiableModuleTest.java	Sat Jun 17 08:03:09 2017 +0100
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package MyPackage;
+
+/**
+ * @test
+ * @summary Verifies the JVMTI IsModifiableModule API
+ * @modules jdk.jdi
+ * @compile IsModifiableModuleTest.java
+ * @run main/othervm/native -agentlib:IsModifiableModuleTest MyPackage.IsModifiableModuleTest
+ */
+
+import java.io.PrintStream;
+
+public class IsModifiableModuleTest {
+
+    static {
+        try {
+            System.loadLibrary("IsModifiableModuleTest");
+        } catch (UnsatisfiedLinkError ule) {
+            System.err.println("Could not load IsModifiableModuleTest library");
+            System.err.println("java.library.path: "
+                + System.getProperty("java.library.path"));
+            throw ule;
+        }
+    }
+
+    native static int check();
+
+    public static void main(String args[]) {
+        int status = check();
+        if (status != 0) {
+            throw new RuntimeException("Non-zero status returned from the agent: " + status);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/serviceability/jvmti/IsModifiableModule/libIsModifiableModuleTest.c	Sat Jun 17 08:03:09 2017 +0100
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "jvmti.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef JNI_ENV_ARG
+
+#ifdef __cplusplus
+#define JNI_ENV_ARG(x, y) y
+#define JNI_ENV_PTR(x) x
+#else
+#define JNI_ENV_ARG(x,y) x, y
+#define JNI_ENV_PTR(x) (*x)
+#endif
+
+#endif
+
+#define TranslateError(err) "JVMTI error"
+
+#define PASSED 0
+#define FAILED 2
+
+static const char *EXC_CNAME = "java/lang/AssertionError";
+
+static jvmtiEnv *jvmti = NULL;
+static jint result = PASSED;
+static jboolean printdump = JNI_FALSE;
+
+static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
+
+JNIEXPORT
+jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
+    return Agent_Initialize(jvm, options, reserved);
+}
+
+JNIEXPORT
+jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
+    return Agent_Initialize(jvm, options, reserved);
+}
+
+JNIEXPORT
+jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
+    return JNI_VERSION_1_8;
+}
+
+static
+jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
+    jint res;
+
+    if (options != NULL && strcmp(options, "printdump") == 0) {
+        printdump = JNI_TRUE;
+    }
+
+    res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
+        JVMTI_VERSION_9);
+    if (res != JNI_OK || jvmti == NULL) {
+        printf("    Error: wrong result of a valid call to GetEnv!\n");
+        return JNI_ERR;
+    }
+
+    return JNI_OK;
+}
+
+static
+jclass find_class(JNIEnv *env, const char* cname) {
+    jclass cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, cname));
+
+    if (cls == NULL) {
+        printf("find_class: Error: FindClass(env, \"%s\") returned NULL\n", cname);
+    }
+    return cls;
+}
+
+static
+jint throw_exc(JNIEnv *env, char *msg) {
+    jclass exc_class = find_class(env, EXC_CNAME);
+
+    if (exc_class == NULL) {
+        printf("throw_exc: Error in find_class(env, \"%s\")\n", EXC_CNAME);
+        return -1;
+    }
+    return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg);
+}
+
+static jobject get_module_by_class_name(JNIEnv *env, const char* cname) {
+    jobject module = NULL;
+    jclass cls = find_class(env, cname);
+
+    printf(">>> getting module by class name: \"%s\"\n", cname);
+    if (cls == NULL) {
+        printf("get_module_by_class_name: Error in find_class(env, \"%s\")\n", cname);
+        return NULL;
+    }
+    module = JNI_ENV_PTR(env)->GetModule(JNI_ENV_ARG(env, cls));
+    if (module == NULL) {
+        printf("get_module_by_class_name: Error in GetModule for class \"%s\"\n", cname);
+    }
+    return module;
+}
+
+static
+jint check_is_modifiable_error_codes(jobject module, jobject not_a_module) {
+    jvmtiError err = JVMTI_ERROR_NONE;
+    jboolean is_modifiable = JNI_FALSE;
+
+    printf(">>> passing a bad module argument to JVMTI IsModifiableModule\n");
+    err = (*jvmti)->IsModifiableModule(jvmti, not_a_module, &is_modifiable);
+    if (err != JVMTI_ERROR_INVALID_MODULE) {
+        printf("    Error #EC0: Did not get expected INVALID_MODULE error code from"
+               " IsModifiableModule: %s (%d)\n", TranslateError(err), err);
+        return FAILED;
+    }
+    printf(">>> passing NULL module argument to JVMTI IsModifiableModule\n");
+    err = (*jvmti)->IsModifiableModule(jvmti, NULL, &is_modifiable);
+    if (err != JVMTI_ERROR_NULL_POINTER) {
+        printf("    Error #EC1: Did not get expected NULL_POINTER error code from"
+               " IsModifiableModule: %s (%d)\n", TranslateError(err), err);
+        return FAILED;
+    }
+    printf(">>> passing NULL status pointer to JVMTI IsModifiableModule\n");
+    err = (*jvmti)->IsModifiableModule(jvmti, module, NULL);
+    if (err != JVMTI_ERROR_NULL_POINTER) {
+        printf("    Error #EC2: Did not get expected NULL_POINTER error code from"
+               " IsModifiableModule: %s (%d)\n", TranslateError(err), err);
+        return FAILED;
+    }
+    return PASSED;
+}
+
+static
+jint check_is_modifiable(jobject module) {
+    jvmtiError err = JVMTI_ERROR_NONE;
+    jboolean is_modifiable = JNI_FALSE;
+
+    printf(">>> checking module %p is modifiable\n", module);
+    err = (*jvmti)->IsModifiableModule(jvmti, module, &is_modifiable);
+    if (err != JVMTI_ERROR_NONE) {
+        printf("    Error in IsModifiableModule for module %p: %s (%d)\n",
+               module, TranslateError(err), err);
+        return FAILED;
+    }
+    if (is_modifiable == JNI_FALSE) {
+        printf("    unexpected non-modifiable status for module: %p\n", module);
+        return FAILED;
+    }
+    return PASSED;
+}
+
+JNIEXPORT jint JNICALL
+Java_MyPackage_IsModifiableModuleTest_check(JNIEnv *env, jclass cls) {
+    jobject module = NULL;
+
+    if (jvmti == NULL) {
+        throw_exc(env, "JVMTI client was not properly loaded!\n");
+        return FAILED;
+    }
+
+    printf("\n*** Testing IsModifiableModule ***\n\n");
+
+    if (check_is_modifiable_error_codes(module, cls) == FAILED) {
+        throw_exc(env, "check #MM0: failed to return expected error code from "
+                      "a bad call to JVMTI IsModifiableModule");
+        return FAILED;
+    }
+
+    module = get_module_by_class_name(env, "java/lang/Class");
+    if (check_is_modifiable(module) == FAILED) {
+        throw_exc(env, "check #MM1: failed to return modifiable module status");
+        return FAILED;
+    }
+
+    module = get_module_by_class_name(env, "com/sun/jdi/VirtualMachine");
+    if (check_is_modifiable(module) == FAILED) {
+        throw_exc(env, "check #MM2: failed to return modifiable module status");
+        return FAILED;
+    }
+
+    module = get_module_by_class_name(env, "MyPackage/IsModifiableModuleTest");
+    if (check_is_modifiable(module) == FAILED) {
+        throw_exc(env, "check #MM3: failed to return modifiable module status");
+        return FAILED;
+    }
+
+    return PASSED;
+}
+
+#ifdef __cplusplus
+}
+#endif