diff --git a/.hgtags b/.hgtags index a470c9afa31..843dc9ea972 100644 --- a/.hgtags +++ b/.hgtags @@ -219,3 +219,6 @@ b72ae39e1329fefae50d4690db4fde43f3841a95 jdk8-b93 49fe9c8049132647ad38837a877dd473e6c9b0e5 jdk8-b95 ea73f01b9053e7165e7ba80f242bafecbc6af712 jdk8-b96 0a85476a0b9cb876d5666d45097dac68bef3fce1 jdk8-b97 +711eb4aa87de68de78250e0549980936bab53d54 jdk8-b98 +2d3875b0d18b3ad1c2bebf385a697e309e4005a4 jdk8-b99 +3d34036aae4ea90b2ca59712d5a69db3221f0875 jdk8-b100 diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 64129189d67..a19c8745350 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -219,3 +219,6 @@ cb51fb4789ac0b8be4056482077ddfb8f3bd3805 jdk8-b91 785d07fe38901ecc1b7e0145e53e1c3da9361fee jdk8-b95 c156084add486f941c12d886a0b1b2854795d557 jdk8-b96 a1c1e8bf71f354f3aec0214cf13d6668811e021d jdk8-b97 +0d0c983a817bbe8518a5ff201306334a8de267f2 jdk8-b98 +59dc9da813794c924a0383c2a6241af94defdfed jdk8-b99 +d2dcb110e9dbaf9903c05b211df800e78e4b394e jdk8-b100 diff --git a/corba/.hgtags b/corba/.hgtags index 28f0b92d88b..9acbd27447b 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -219,3 +219,6 @@ c8286839d0df04aba819ec4bef12b86babccf30e jdk8-b90 2cf36f43df36137980d9828cec27003ec10daeee jdk8-b95 3357c2776431d51a8de326a85e0f41420e40774f jdk8-b96 469995a8e97424f450c880606d689bf345277b19 jdk8-b97 +3370fb6146e47a6cc05a213fc213e12fc0a38d07 jdk8-b98 +3f67804ab61303782df57e54989ef5e0e4629beb jdk8-b99 +8d492f1dfd1b131a4c7886ee6b59528609f7e4fe jdk8-b100 diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 89d305f1d94..c802f848b65 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -357,3 +357,9 @@ e6a4b8c71fa6f225bd989a34de2d0d0a656a8be8 jdk8-b96 2b9380b0bf0b649f40704735773e8956c2d88ba0 hs25-b39 d197d377ab2e016d024e8c86cb06a57bd7eae590 jdk8-b97 c9dd82da51ed34a28f7c6b3245163ee962e94572 hs25-b40 +30b5b75c42ac5174b640fbef8aa87527668e8400 jdk8-b98 +2b9946e10587f74ef75ae8145bea484df4a2738b hs25-b41 +81b6cb70717c66375846b78bb174594ec3aa998e jdk8-b99 +9f71e36a471ae4a668e08827d33035963ed10c08 hs25-b42 +5787fac72e760c6a5fd9efa113b0c75caf554136 jdk8-b100 +46487ba40ff225654d0c51787ed3839bafcbd9f3 hs25-b43 diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java index f7cfb046d7d..9530cdd977a 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ArrayKlass.java @@ -49,7 +49,6 @@ public class ArrayKlass extends Klass { higherDimension = new MetadataField(type.getAddressField("_higher_dimension"), 0); lowerDimension = new MetadataField(type.getAddressField("_lower_dimension"), 0); vtableLen = new CIntField(type.getCIntegerField("_vtable_len"), 0); - allocSize = new CIntField(type.getCIntegerField("_alloc_size"), 0); componentMirror = new OopField(type.getOopField("_component_mirror"), 0); javaLangCloneableName = null; javaLangObjectName = null; @@ -64,7 +63,6 @@ public class ArrayKlass extends Klass { private static MetadataField higherDimension; private static MetadataField lowerDimension; private static CIntField vtableLen; - private static CIntField allocSize; private static OopField componentMirror; public Klass getJavaSuper() { @@ -76,7 +74,6 @@ public class ArrayKlass extends Klass { public Klass getHigherDimension() { return (Klass) higherDimension.getValue(this); } public Klass getLowerDimension() { return (Klass) lowerDimension.getValue(this); } public long getVtableLen() { return vtableLen.getValue(this); } - public long getAllocSize() { return allocSize.getValue(this); } public Oop getComponentMirror() { return componentMirror.getValue(this); } // constant class names - javaLangCloneable, javaIoSerializable, javaLangObject @@ -147,7 +144,6 @@ public class ArrayKlass extends Klass { visitor.doMetadata(higherDimension, true); visitor.doMetadata(lowerDimension, true); visitor.doCInt(vtableLen, true); - visitor.doCInt(allocSize, true); visitor.doOop(componentMirror, true); } } diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java index 63ac006ebb9..19a3668c7f3 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/Klass.java @@ -57,7 +57,6 @@ public class Klass extends Metadata implements ClassConstants { accessFlags = new CIntField(type.getCIntegerField("_access_flags"), 0); subklass = new MetadataField(type.getAddressField("_subklass"), 0); nextSibling = new MetadataField(type.getAddressField("_next_sibling"), 0); - allocCount = new CIntField(type.getCIntegerField("_alloc_count"), 0); LH_INSTANCE_SLOW_PATH_BIT = db.lookupIntConstant("Klass::_lh_instance_slow_path_bit").intValue(); LH_LOG2_ELEMENT_SIZE_SHIFT = db.lookupIntConstant("Klass::_lh_log2_element_size_shift").intValue(); @@ -87,7 +86,6 @@ public class Klass extends Metadata implements ClassConstants { private static CIntField accessFlags; private static MetadataField subklass; private static MetadataField nextSibling; - private static CIntField allocCount; private Address getValue(AddressField field) { return addr.getAddressAt(field.getOffset()); @@ -108,7 +106,6 @@ public class Klass extends Metadata implements ClassConstants { public AccessFlags getAccessFlagsObj(){ return new AccessFlags(getAccessFlags()); } public Klass getSubklassKlass() { return (Klass) subklass.getValue(this); } public Klass getNextSiblingKlass() { return (Klass) nextSibling.getValue(this); } - public long getAllocCount() { return allocCount.getValue(this); } // computed access flags - takes care of inner classes etc. // This is closer to actual source level than getAccessFlags() etc. @@ -172,7 +169,6 @@ public class Klass extends Metadata implements ClassConstants { visitor.doCInt(accessFlags, true); visitor.doMetadata(subklass, true); visitor.doMetadata(nextSibling, true); - visitor.doCInt(allocCount, true); } public long getObjectSize() { diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-debug b/hotspot/make/bsd/makefiles/mapfile-vers-debug index d371239ab06..9a8fc822bc4 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-debug +++ b/hotspot/make/bsd/makefiles/mapfile-vers-debug @@ -221,7 +221,6 @@ _JVM_SetLength _JVM_SetNativeThreadName _JVM_SetPrimitiveArrayElement - _JVM_SetProtectionDomain _JVM_SetSockOpt _JVM_SetThreadPriority _JVM_Sleep diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-product b/hotspot/make/bsd/makefiles/mapfile-vers-product index ba9cb516620..d446cc81d84 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-product +++ b/hotspot/make/bsd/makefiles/mapfile-vers-product @@ -221,7 +221,6 @@ _JVM_SetLength _JVM_SetNativeThreadName _JVM_SetPrimitiveArrayElement - _JVM_SetProtectionDomain _JVM_SetSockOpt _JVM_SetThreadPriority _JVM_Sleep diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version index b597ff990be..b373e5fb0f6 100644 --- a/hotspot/make/hotspot_version +++ b/hotspot/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2013 HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=40 +HS_BUILD_NUMBER=43 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff --git a/hotspot/make/linux/makefiles/mapfile-vers-debug b/hotspot/make/linux/makefiles/mapfile-vers-debug index b18fb74fd9a..68be2ffefb3 100644 --- a/hotspot/make/linux/makefiles/mapfile-vers-debug +++ b/hotspot/make/linux/makefiles/mapfile-vers-debug @@ -223,7 +223,6 @@ SUNWprivate_1.1 { JVM_SetLength; JVM_SetNativeThreadName; JVM_SetPrimitiveArrayElement; - JVM_SetProtectionDomain; JVM_SetSockOpt; JVM_SetThreadPriority; JVM_Sleep; diff --git a/hotspot/make/linux/makefiles/mapfile-vers-product b/hotspot/make/linux/makefiles/mapfile-vers-product index 168d84c1e90..4641af0af20 100644 --- a/hotspot/make/linux/makefiles/mapfile-vers-product +++ b/hotspot/make/linux/makefiles/mapfile-vers-product @@ -223,7 +223,6 @@ SUNWprivate_1.1 { JVM_SetLength; JVM_SetNativeThreadName; JVM_SetPrimitiveArrayElement; - JVM_SetProtectionDomain; JVM_SetSockOpt; JVM_SetThreadPriority; JVM_Sleep; diff --git a/hotspot/make/linux/makefiles/vm.make b/hotspot/make/linux/makefiles/vm.make index 3c3bd2e2d16..7df141f8895 100644 --- a/hotspot/make/linux/makefiles/vm.make +++ b/hotspot/make/linux/makefiles/vm.make @@ -46,6 +46,7 @@ ifeq ($(findstring true, $(JVM_VARIANT_ZERO) $(JVM_VARIANT_ZEROSHARK)), true) include $(MAKEFILES_DIR)/zeroshark.make else include $(MAKEFILES_DIR)/$(BUILDARCH).make + -include $(HS_ALT_MAKE)/$(Platform_os_family)/makefiles/$(BUILDARCH).make endif # set VPATH so make knows where to look for source files @@ -211,6 +212,12 @@ ifeq ($(Platform_arch_model), x86_64) Src_Files_EXCLUDE += \*x86_32\* endif +# Alternate vm.make +# This has to be included here to allow changes to the source +# directories and excluded files before they are expanded +# by the definition of Src_Files. +-include $(HS_ALT_MAKE)/$(Platform_os_family)/makefiles/vm.make + # Locate all source files in the given directory, excluding files in Src_Files_EXCLUDE. define findsrc $(notdir $(shell find $(1)/. ! -name . -prune \ @@ -380,4 +387,4 @@ build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(BUILDLIBSAPROC) dtraceChe install: install_jvm install_jsig install_saproc -.PHONY: default build install install_jvm +.PHONY: default build install install_jvm $(HS_ALT_MAKE)/$(Platform_os_family)/makefiles/$(BUILDARCH).make $(HS_ALT_MAKE)/$(Platform_os_family)/makefiles/vm.make diff --git a/hotspot/make/solaris/makefiles/mapfile-vers b/hotspot/make/solaris/makefiles/mapfile-vers index 1a0b572a5c6..01b31b6fffb 100644 --- a/hotspot/make/solaris/makefiles/mapfile-vers +++ b/hotspot/make/solaris/makefiles/mapfile-vers @@ -223,7 +223,6 @@ SUNWprivate_1.1 { JVM_SetLength; JVM_SetNativeThreadName; JVM_SetPrimitiveArrayElement; - JVM_SetProtectionDomain; JVM_SetSockOpt; JVM_SetThreadPriority; JVM_Sleep; diff --git a/hotspot/make/windows/makefiles/compile.make b/hotspot/make/windows/makefiles/compile.make index ea16fca3dde..6f8dcce3406 100644 --- a/hotspot/make/windows/makefiles/compile.make +++ b/hotspot/make/windows/makefiles/compile.make @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2013, 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 @@ -110,6 +110,7 @@ CXX_FLAGS=$(CXX_FLAGS) /D TARGET_COMPILER_visCPP # 1400 is for VS2005 # 1500 is for VS2008 # 1600 is for VS2010 +# 1700 is for VS2012 # Do not confuse this MSC_VER with the predefined macro _MSC_VER that the # compiler provides, when MSC_VER==1399, _MSC_VER will be 1400. # Normally they are the same, but a pre-release of the VS2005 compilers @@ -142,6 +143,9 @@ COMPILER_NAME=VS2008 !if "$(MSC_VER)" == "1600" COMPILER_NAME=VS2010 !endif +!if "$(MSC_VER)" == "1700" +COMPILER_NAME=VS2012 +!endif !endif # By default, we do not want to use the debug version of the msvcrt.dll file @@ -151,9 +155,13 @@ MS_RUNTIME_OPTION = /MD MS_RUNTIME_OPTION = /MTd /D "_DEBUG" !endif +# VS2012 and later won't work with: +# /D _STATIC_CPPLIB /D _DISABLE_DEPRECATE_STATIC_CPPLIB +!if "$(MSC_VER)" < "1700" # Always add the _STATIC_CPPLIB flag STATIC_CPPLIB_OPTION = /D _STATIC_CPPLIB /D _DISABLE_DEPRECATE_STATIC_CPPLIB MS_RUNTIME_OPTION = $(MS_RUNTIME_OPTION) $(STATIC_CPPLIB_OPTION) +!endif CXX_FLAGS=$(CXX_FLAGS) $(MS_RUNTIME_OPTION) # How /GX option is spelled @@ -221,6 +229,22 @@ LD_FLAGS = /SAFESEH $(LD_FLAGS) !endif !endif +!if "$(COMPILER_NAME)" == "VS2012" +PRODUCT_OPT_OPTION = /O2 /Oy- +FASTDEBUG_OPT_OPTION = /O2 /Oy- +DEBUG_OPT_OPTION = /Od +GX_OPTION = /EHsc +LD_FLAGS = /manifest $(LD_FLAGS) +# Manifest Tool - used in VS2005 and later to adjust manifests stored +# as resources inside build artifacts. +!if "x$(MT)" == "x" +MT=mt.exe +!endif +!if "$(BUILDARCH)" == "i486" +LD_FLAGS = /SAFESEH $(LD_FLAGS) +!endif +!endif + # If NO_OPTIMIZATIONS is defined in the environment, turn everything off !ifdef NO_OPTIMIZATIONS PRODUCT_OPT_OPTION = $(DEBUG_OPT_OPTION) diff --git a/hotspot/make/windows/makefiles/sanity.make b/hotspot/make/windows/makefiles/sanity.make index 86c6b59aefe..b502cfaa65f 100644 --- a/hotspot/make/windows/makefiles/sanity.make +++ b/hotspot/make/windows/makefiles/sanity.make @@ -1,5 +1,5 @@ # -# Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2013, 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 @@ -27,9 +27,9 @@ all: checkCL checkLink checkCL: - @ if "$(MSC_VER)" NEQ "1310" if "$(MSC_VER)" NEQ "1399" if "$(MSC_VER)" NEQ "1400" if "$(MSC_VER)" NEQ "1500" if "$(MSC_VER)" NEQ "1600" \ + @ if "$(MSC_VER)" NEQ "1310" if "$(MSC_VER)" NEQ "1399" if "$(MSC_VER)" NEQ "1400" if "$(MSC_VER)" NEQ "1500" if "$(MSC_VER)" NEQ "1600" if "$(MSC_VER)" NEQ "1700" \ echo *** WARNING *** unrecognized cl.exe version $(MSC_VER) ($(RAW_MSC_VER)). Use FORCE_MSC_VER to override automatic detection. checkLink: - @ if "$(LD_VER)" NEQ "710" if "$(LD_VER)" NEQ "800" if "$(LD_VER)" NEQ "900" if "$(LD_VER)" NEQ "1000" \ + @ if "$(LD_VER)" NEQ "710" if "$(LD_VER)" NEQ "800" if "$(LD_VER)" NEQ "900" if "$(LD_VER)" NEQ "1000" if "$(LD_VER)" NEQ "1100" \ echo *** WARNING *** unrecognized link.exe version $(LD_VER) ($(RAW_LD_VER)). Use FORCE_LD_VER to override automatic detection. diff --git a/hotspot/make/windows/makefiles/vm.make b/hotspot/make/windows/makefiles/vm.make index 54ba1eef5b8..b76443774de 100644 --- a/hotspot/make/windows/makefiles/vm.make +++ b/hotspot/make/windows/makefiles/vm.make @@ -132,6 +132,10 @@ CXX_DONT_USE_PCH=/D DONT_USE_PRECOMPILED_HEADER !if "$(USE_PRECOMPILED_HEADER)" != "0" CXX_USE_PCH=/Fp"vm.pch" /Yu"precompiled.hpp" +!if "$(COMPILER_NAME)" == "VS2012" +# VS2012 requires this object file to be listed: +LD_FLAGS=$(LD_FLAGS) _build_pch_file.obj +!endif !else CXX_USE_PCH=$(CXX_DONT_USE_PCH) !endif diff --git a/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp b/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp index 71655f6d760..774e8f3f0f5 100644 --- a/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp +++ b/hotspot/src/cpu/sparc/vm/frame_sparc.inline.hpp @@ -240,10 +240,10 @@ inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const { #endif // CC_INTERP -inline JavaCallWrapper* frame::entry_frame_call_wrapper() const { +inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { // note: adjust this code if the link argument in StubGenerator::call_stub() changes! const Argument link = Argument(0, false); - return (JavaCallWrapper*)sp()[link.as_in().as_register()->sp_offset_in_saved_window()]; + return (JavaCallWrapper**)&sp()[link.as_in().as_register()->sp_offset_in_saved_window()]; } diff --git a/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp index 494c1bc405a..214940cdbfb 100644 --- a/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/stubGenerator_sparc.cpp @@ -410,6 +410,51 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Safefetch stubs. + void generate_safefetch(const char* name, int size, address* entry, + address* fault_pc, address* continuation_pc) { + // safefetch signatures: + // int SafeFetch32(int* adr, int errValue); + // intptr_t SafeFetchN (intptr_t* adr, intptr_t errValue); + // + // arguments: + // o0 = adr + // o1 = errValue + // + // result: + // o0 = *adr or errValue + + StubCodeMark mark(this, "StubRoutines", name); + + // Entry point, pc or function descriptor. + __ align(CodeEntryAlignment); + *entry = __ pc(); + + __ mov(O0, G1); // g1 = o0 + __ mov(O1, O0); // o0 = o1 + // Load *adr into c_rarg1, may fault. + *fault_pc = __ pc(); + switch (size) { + case 4: + // int32_t + __ ldsw(G1, 0, O0); // o0 = [g1] + break; + case 8: + // int64_t + __ ldx(G1, 0, O0); // o0 = [g1] + break; + default: + ShouldNotReachHere(); + } + + // return errValue or *adr + *continuation_pc = __ pc(); + // By convention with the trap handler we ensure there is a non-CTI + // instruction in the trap shadow. + __ nop(); + __ retl(); + __ delayed()->nop(); + } //------------------------------------------------------------------------------------------------------------------------ // Continuation point for throwing of implicit exceptions that are not handled in @@ -3315,6 +3360,14 @@ class StubGenerator: public StubCodeGenerator { // Don't initialize the platform math functions since sparc // doesn't have intrinsics for these operations. + + // Safefetch stubs. + generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry, + &StubRoutines::_safefetch32_fault_pc, + &StubRoutines::_safefetch32_continuation_pc); + generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, + &StubRoutines::_safefetchN_fault_pc, + &StubRoutines::_safefetchN_continuation_pc); } diff --git a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp index b15b00be586..3c5c225c399 100644 --- a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp +++ b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp @@ -272,11 +272,10 @@ inline jint frame::interpreter_frame_expression_stack_direction() { return -1; } // Entry frames -inline JavaCallWrapper* frame::entry_frame_call_wrapper() const { - return (JavaCallWrapper*)at(entry_frame_call_wrapper_offset); +inline JavaCallWrapper** frame::entry_frame_call_wrapper_addr() const { + return (JavaCallWrapper**)addr_at(entry_frame_call_wrapper_offset); } - // Compiled frames inline int frame::local_offset_for_compiler(int local_index, int nof_args, int max_nof_locals, int max_nof_monitors) { diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp index 82e4183ef47..a8abfea6bcd 100644 --- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_32.cpp @@ -2766,6 +2766,39 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Safefetch stubs. + void generate_safefetch(const char* name, int size, address* entry, + address* fault_pc, address* continuation_pc) { + // safefetch signatures: + // int SafeFetch32(int* adr, int errValue); + // intptr_t SafeFetchN (intptr_t* adr, intptr_t errValue); + + StubCodeMark mark(this, "StubRoutines", name); + + // Entry point, pc or function descriptor. + *entry = __ pc(); + + __ movl(rax, Address(rsp, 0x8)); + __ movl(rcx, Address(rsp, 0x4)); + // Load *adr into eax, may fault. + *fault_pc = __ pc(); + switch (size) { + case 4: + // int32_t + __ movl(rax, Address(rcx, 0)); + break; + case 8: + // int64_t + Unimplemented(); + break; + default: + ShouldNotReachHere(); + } + + // Return errValue or *adr. + *continuation_pc = __ pc(); + __ ret(0); + } public: // Information about frame layout at time of blocking runtime call. @@ -2978,6 +3011,14 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt(); } + + // Safefetch stubs. + generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry, + &StubRoutines::_safefetch32_fault_pc, + &StubRoutines::_safefetch32_continuation_pc); + StubRoutines::_safefetchN_entry = StubRoutines::_safefetch32_entry; + StubRoutines::_safefetchN_fault_pc = StubRoutines::_safefetch32_fault_pc; + StubRoutines::_safefetchN_continuation_pc = StubRoutines::_safefetch32_continuation_pc; } diff --git a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp index 2d94642f828..e38139d28ec 100644 --- a/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp @@ -279,7 +279,7 @@ class StubGenerator: public StubCodeGenerator { __ stmxcsr(mxcsr_save); __ movl(rax, mxcsr_save); __ andl(rax, MXCSR_MASK); // Only check control and mask bits - ExternalAddress mxcsr_std(StubRoutines::x86::mxcsr_std()); + ExternalAddress mxcsr_std(StubRoutines::addr_mxcsr_std()); __ cmp32(rax, mxcsr_std); __ jcc(Assembler::equal, skip_ldmx); __ ldmxcsr(mxcsr_std); @@ -729,17 +729,18 @@ class StubGenerator: public StubCodeGenerator { if (CheckJNICalls) { Label ok_ret; + ExternalAddress mxcsr_std(StubRoutines::addr_mxcsr_std()); __ push(rax); __ subptr(rsp, wordSize); // allocate a temp location __ stmxcsr(mxcsr_save); __ movl(rax, mxcsr_save); __ andl(rax, MXCSR_MASK); // Only check control and mask bits - __ cmpl(rax, *(int *)(StubRoutines::x86::mxcsr_std())); + __ cmp32(rax, mxcsr_std); __ jcc(Assembler::equal, ok_ret); __ warn("MXCSR changed by native JNI code, use -XX:+RestoreMXCSROnJNICall"); - __ ldmxcsr(ExternalAddress(StubRoutines::x86::mxcsr_std())); + __ ldmxcsr(mxcsr_std); __ bind(ok_ret); __ addptr(rsp, wordSize); @@ -3357,7 +3358,45 @@ class StubGenerator: public StubCodeGenerator { return start; } + // Safefetch stubs. + void generate_safefetch(const char* name, int size, address* entry, + address* fault_pc, address* continuation_pc) { + // safefetch signatures: + // int SafeFetch32(int* adr, int errValue); + // intptr_t SafeFetchN (intptr_t* adr, intptr_t errValue); + // + // arguments: + // c_rarg0 = adr + // c_rarg1 = errValue + // + // result: + // PPC_RET = *adr or errValue + StubCodeMark mark(this, "StubRoutines", name); + + // Entry point, pc or function descriptor. + *entry = __ pc(); + + // Load *adr into c_rarg1, may fault. + *fault_pc = __ pc(); + switch (size) { + case 4: + // int32_t + __ movl(c_rarg1, Address(c_rarg0, 0)); + break; + case 8: + // int64_t + __ movq(c_rarg1, Address(c_rarg0, 0)); + break; + default: + ShouldNotReachHere(); + } + + // return errValue or *adr + *continuation_pc = __ pc(); + __ movq(rax, c_rarg1); + __ ret(0); + } // This is a version of CBC/AES Decrypt which does 4 blocks in a loop at a time // to hide instruction latency @@ -3729,12 +3768,35 @@ class StubGenerator: public StubCodeGenerator { return stub->entry_point(); } + void create_control_words() { + // Round to nearest, 53-bit mode, exceptions masked + StubRoutines::_fpu_cntrl_wrd_std = 0x027F; + // Round to zero, 53-bit mode, exception mased + StubRoutines::_fpu_cntrl_wrd_trunc = 0x0D7F; + // Round to nearest, 24-bit mode, exceptions masked + StubRoutines::_fpu_cntrl_wrd_24 = 0x007F; + // Round to nearest, 64-bit mode, exceptions masked + StubRoutines::_fpu_cntrl_wrd_64 = 0x037F; + // Round to nearest, 64-bit mode, exceptions masked + StubRoutines::_mxcsr_std = 0x1F80; + // Note: the following two constants are 80-bit values + // layout is critical for correct loading by FPU. + // Bias for strict fp multiply/divide + StubRoutines::_fpu_subnormal_bias1[0]= 0x00000000; // 2^(-15360) == 0x03ff 8000 0000 0000 0000 + StubRoutines::_fpu_subnormal_bias1[1]= 0x80000000; + StubRoutines::_fpu_subnormal_bias1[2]= 0x03ff; + // Un-Bias for strict fp multiply/divide + StubRoutines::_fpu_subnormal_bias2[0]= 0x00000000; // 2^(+15360) == 0x7bff 8000 0000 0000 0000 + StubRoutines::_fpu_subnormal_bias2[1]= 0x80000000; + StubRoutines::_fpu_subnormal_bias2[2]= 0x7bff; + } + // Initialization void generate_initial() { // Generates all stubs and initializes the entry points - // This platform-specific stub is needed by generate_call_stub() - StubRoutines::x86::_mxcsr_std = generate_fp_mask("mxcsr_std", 0x0000000000001F80); + // This platform-specific settings are needed by generate_call_stub() + create_control_words(); // entry points that exist in all platforms Note: This is code // that could be shared among different platforms - however the @@ -3833,6 +3895,14 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); } + + // Safefetch stubs. + generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry, + &StubRoutines::_safefetch32_fault_pc, + &StubRoutines::_safefetch32_continuation_pc); + generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, + &StubRoutines::_safefetchN_fault_pc, + &StubRoutines::_safefetchN_continuation_pc); } public: diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp index 5c11734cc97..bf539083e90 100644 --- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp @@ -42,4 +42,3 @@ address StubRoutines::x86::_float_sign_mask = NULL; address StubRoutines::x86::_float_sign_flip = NULL; address StubRoutines::x86::_double_sign_mask = NULL; address StubRoutines::x86::_double_sign_flip = NULL; -address StubRoutines::x86::_mxcsr_std = NULL; diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp index d63e9fdf57f..205bce4eb76 100644 --- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp +++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp @@ -52,7 +52,6 @@ class x86 { static address _float_sign_flip; static address _double_sign_mask; static address _double_sign_flip; - static address _mxcsr_std; public: @@ -106,11 +105,6 @@ class x86 { return _double_sign_flip; } - static address mxcsr_std() - { - return _mxcsr_std; - } - # include "stubRoutines_x86.hpp" }; diff --git a/hotspot/src/os/bsd/vm/attachListener_bsd.cpp b/hotspot/src/os/bsd/vm/attachListener_bsd.cpp index ee4feb8d046..6f128f2543b 100644 --- a/hotspot/src/os/bsd/vm/attachListener_bsd.cpp +++ b/hotspot/src/os/bsd/vm/attachListener_bsd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, 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 @@ -437,6 +437,30 @@ AttachOperation* AttachListener::dequeue() { return op; } + +// Performs initialization at vm startup +// For BSD we remove any stale .java_pid file which could cause +// an attaching process to think we are ready to receive on the +// domain socket before we are properly initialized + +void AttachListener::vm_start() { + char fn[UNIX_PATH_MAX]; + struct stat64 st; + int ret; + + int n = snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", + os::get_temp_directory(), os::current_process_id()); + assert(n < (int)UNIX_PATH_MAX, "java_pid file name buffer overflow"); + + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == 0) { + ret = ::unlink(fn); + if (ret == -1) { + debug_only(warning("failed to remove stale attach pid file at %s", fn)); + } + } +} + int AttachListener::pd_init() { JavaThread* thread = JavaThread::current(); ThreadBlockInVM tbivm(thread); diff --git a/hotspot/src/os/bsd/vm/os_bsd.cpp b/hotspot/src/os/bsd/vm/os_bsd.cpp index 2db740583e8..55043ad243a 100644 --- a/hotspot/src/os/bsd/vm/os_bsd.cpp +++ b/hotspot/src/os/bsd/vm/os_bsd.cpp @@ -1234,12 +1234,13 @@ bool os::address_is_in_vm(address addr) { Dl_info dlinfo; if (libjvm_base_addr == NULL) { - dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo); - libjvm_base_addr = (address)dlinfo.dli_fbase; + if (dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo) != 0) { + libjvm_base_addr = (address)dlinfo.dli_fbase; + } assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm"); } - if (dladdr((void *)addr, &dlinfo)) { + if (dladdr((void *)addr, &dlinfo) != 0) { if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true; } @@ -1251,35 +1252,40 @@ bool os::address_is_in_vm(address addr) { bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + Dl_info dlinfo; char localbuf[MACH_MAXSYMLEN]; - // dladdr will find names of dynamic functions only, but does - // it set dli_fbase with mach_header address when it "fails" ? - if (dladdr((void*)addr, &dlinfo) && dlinfo.dli_sname != NULL) { - if (buf != NULL) { - if(!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { + if (dladdr((void*)addr, &dlinfo) != 0) { + // see if we have a matching symbol + if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) { + if (!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname); } + if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; + return true; } - if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; - return true; - } else if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != 0) { - if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), - buf, buflen, offset, dlinfo.dli_fname)) { - return true; + // no matching symbol so try for just file info + if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) { + if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), + buf, buflen, offset, dlinfo.dli_fname)) { + return true; + } } - } - // Handle non-dymanic manually: - if (dlinfo.dli_fbase != NULL && - Decoder::decode(addr, localbuf, MACH_MAXSYMLEN, offset, dlinfo.dli_fbase)) { - if(!Decoder::demangle(localbuf, buf, buflen)) { - jio_snprintf(buf, buflen, "%s", localbuf); + // Handle non-dynamic manually: + if (dlinfo.dli_fbase != NULL && + Decoder::decode(addr, localbuf, MACH_MAXSYMLEN, offset, + dlinfo.dli_fbase)) { + if (!Decoder::demangle(localbuf, buf, buflen)) { + jio_snprintf(buf, buflen, "%s", localbuf); + } + return true; } - return true; } - if (buf != NULL) buf[0] = '\0'; + buf[0] = '\0'; if (offset != NULL) *offset = -1; return false; } @@ -1287,17 +1293,24 @@ bool os::dll_address_to_function_name(address addr, char *buf, // ported from solaris version bool os::dll_address_to_library_name(address addr, char* buf, int buflen, int* offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + Dl_info dlinfo; - if (dladdr((void*)addr, &dlinfo)){ - if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); - if (offset) *offset = addr - (address)dlinfo.dli_fbase; - return true; - } else { - if (buf) buf[0] = '\0'; - if (offset) *offset = -1; - return false; + if (dladdr((void*)addr, &dlinfo) != 0) { + if (dlinfo.dli_fname != NULL) { + jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); + } + if (dlinfo.dli_fbase != NULL && offset != NULL) { + *offset = addr - (address)dlinfo.dli_fbase; + } + return true; } + + buf[0] = '\0'; + if (offset) *offset = -1; + return false; } // Loads .dll/.so and @@ -1520,49 +1533,50 @@ static bool _print_ascii_file(const char* filename, outputStream* st) { } void os::print_dll_info(outputStream *st) { - st->print_cr("Dynamic libraries:"); + st->print_cr("Dynamic libraries:"); #ifdef RTLD_DI_LINKMAP - Dl_info dli; - void *handle; - Link_map *map; - Link_map *p; + Dl_info dli; + void *handle; + Link_map *map; + Link_map *p; - if (!dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli)) { - st->print_cr("Error: Cannot print dynamic libraries."); - return; - } - handle = dlopen(dli.dli_fname, RTLD_LAZY); - if (handle == NULL) { - st->print_cr("Error: Cannot print dynamic libraries."); - return; - } - dlinfo(handle, RTLD_DI_LINKMAP, &map); - if (map == NULL) { - st->print_cr("Error: Cannot print dynamic libraries."); - return; - } + if (dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli) == 0 || + dli.dli_fname == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + handle = dlopen(dli.dli_fname, RTLD_LAZY); + if (handle == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + dlinfo(handle, RTLD_DI_LINKMAP, &map); + if (map == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } - while (map->l_prev != NULL) - map = map->l_prev; + while (map->l_prev != NULL) + map = map->l_prev; - while (map != NULL) { - st->print_cr(PTR_FORMAT " \t%s", map->l_addr, map->l_name); - map = map->l_next; - } + while (map != NULL) { + st->print_cr(PTR_FORMAT " \t%s", map->l_addr, map->l_name); + map = map->l_next; + } - dlclose(handle); + dlclose(handle); #elif defined(__APPLE__) - uint32_t count; - uint32_t i; + uint32_t count; + uint32_t i; - count = _dyld_image_count(); - for (i = 1; i < count; i++) { - const char *name = _dyld_get_image_name(i); - intptr_t slide = _dyld_get_image_vmaddr_slide(i); - st->print_cr(PTR_FORMAT " \t%s", slide, name); - } + count = _dyld_image_count(); + for (i = 1; i < count; i++) { + const char *name = _dyld_get_image_name(i); + intptr_t slide = _dyld_get_image_vmaddr_slide(i); + st->print_cr(PTR_FORMAT " \t%s", slide, name); + } #else - st->print_cr("Error: Cannot print dynamic libraries."); + st->print_cr("Error: Cannot print dynamic libraries."); #endif } @@ -1707,8 +1721,11 @@ void os::jvm_path(char *buf, jint buflen) { bool ret = dll_address_to_library_name( CAST_FROM_FN_PTR(address, os::jvm_path), dli_fname, sizeof(dli_fname), NULL); - assert(ret != 0, "cannot locate libjvm"); - char *rp = realpath(dli_fname, buf); + assert(ret, "cannot locate libjvm"); + char *rp = NULL; + if (ret && dli_fname[0] != '\0') { + rp = realpath(dli_fname, buf); + } if (rp == NULL) return; @@ -3747,20 +3764,20 @@ int os::Bsd::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, bool os::find(address addr, outputStream* st) { Dl_info dlinfo; memset(&dlinfo, 0, sizeof(dlinfo)); - if (dladdr(addr, &dlinfo)) { + if (dladdr(addr, &dlinfo) != 0) { st->print(PTR_FORMAT ": ", addr); - if (dlinfo.dli_sname != NULL) { + if (dlinfo.dli_sname != NULL && dlinfo.dli_saddr != NULL) { st->print("%s+%#x", dlinfo.dli_sname, addr - (intptr_t)dlinfo.dli_saddr); - } else if (dlinfo.dli_fname) { + } else if (dlinfo.dli_fbase != NULL) { st->print("", addr - (intptr_t)dlinfo.dli_fbase); } else { st->print(""); } - if (dlinfo.dli_fname) { + if (dlinfo.dli_fname != NULL) { st->print(" in %s", dlinfo.dli_fname); } - if (dlinfo.dli_fbase) { + if (dlinfo.dli_fbase != NULL) { st->print(" at " PTR_FORMAT, dlinfo.dli_fbase); } st->cr(); @@ -3773,7 +3790,7 @@ bool os::find(address addr, outputStream* st) { if (!lowest) lowest = (address) dlinfo.dli_fbase; if (begin < lowest) begin = lowest; Dl_info dlinfo2; - if (dladdr(end, &dlinfo2) && dlinfo2.dli_saddr != dlinfo.dli_saddr + if (dladdr(end, &dlinfo2) != 0 && dlinfo2.dli_saddr != dlinfo.dli_saddr && end > dlinfo2.dli_saddr && dlinfo2.dli_saddr > begin) end = (address) dlinfo2.dli_saddr; Disassembler::decode(begin, end, st); diff --git a/hotspot/src/os/linux/vm/attachListener_linux.cpp b/hotspot/src/os/linux/vm/attachListener_linux.cpp index 035d298a36c..700a09ff0c5 100644 --- a/hotspot/src/os/linux/vm/attachListener_linux.cpp +++ b/hotspot/src/os/linux/vm/attachListener_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, 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 @@ -432,6 +432,30 @@ AttachOperation* AttachListener::dequeue() { return op; } + +// Performs initialization at vm startup +// For Linux we remove any stale .java_pid file which could cause +// an attaching process to think we are ready to receive on the +// domain socket before we are properly initialized + +void AttachListener::vm_start() { + char fn[UNIX_PATH_MAX]; + struct stat64 st; + int ret; + + int n = snprintf(fn, UNIX_PATH_MAX, "%s/.java_pid%d", + os::get_temp_directory(), os::current_process_id()); + assert(n < (int)UNIX_PATH_MAX, "java_pid file name buffer overflow"); + + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == 0) { + ret = ::unlink(fn); + if (ret == -1) { + debug_only(warning("failed to remove stale attach pid file at %s", fn)); + } + } +} + int AttachListener::pd_init() { JavaThread* thread = JavaThread::current(); ThreadBlockInVM tbivm(thread); diff --git a/hotspot/src/os/linux/vm/jsig.c b/hotspot/src/os/linux/vm/jsig.c index 13ddb668341..ae2f25b33c4 100644 --- a/hotspot/src/os/linux/vm/jsig.c +++ b/hotspot/src/os/linux/vm/jsig.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, 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 @@ -107,7 +107,7 @@ static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) { signal_lock(); - sigused = (MASK(sig) & jvmsigs) != 0; + sigused = (sig < MAXSIGNUM) && ((MASK(sig) & jvmsigs) != 0); if (jvm_signal_installed && sigused) { /* jvm has installed its signal handler for this signal. */ /* Save the handler. Don't really install it. */ @@ -116,7 +116,7 @@ static sa_handler_t set_signal(int sig, sa_handler_t disp, bool is_sigset) { signal_unlock(); return oldhandler; - } else if (jvm_signal_installing) { + } else if (sig < MAXSIGNUM && jvm_signal_installing) { /* jvm is installing its signal handlers. Install the new * handlers and save the old ones. jvm uses sigaction(). * Leave the piece here just in case. */ @@ -165,7 +165,7 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { signal_lock(); - sigused = (MASK(sig) & jvmsigs) != 0; + sigused = (sig < MAXSIGNUM) && ((MASK(sig) & jvmsigs) != 0); if (jvm_signal_installed && sigused) { /* jvm has installed its signal handler for this signal. */ /* Save the handler. Don't really install it. */ @@ -178,7 +178,7 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { signal_unlock(); return 0; - } else if (jvm_signal_installing) { + } else if (sig < MAXSIGNUM && jvm_signal_installing) { /* jvm is installing its signal handlers. Install the new * handlers and save the old ones. */ res = call_os_sigaction(sig, act, &oldAct); diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index 53bfe4e7369..df58be635d1 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -1682,12 +1682,13 @@ bool os::address_is_in_vm(address addr) { Dl_info dlinfo; if (libjvm_base_addr == NULL) { - dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo); - libjvm_base_addr = (address)dlinfo.dli_fbase; + if (dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo) != 0) { + libjvm_base_addr = (address)dlinfo.dli_fbase; + } assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm"); } - if (dladdr((void *)addr, &dlinfo)) { + if (dladdr((void *)addr, &dlinfo) != 0) { if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true; } @@ -1696,24 +1697,30 @@ bool os::address_is_in_vm(address addr) { bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + Dl_info dlinfo; - if (dladdr((void*)addr, &dlinfo) && dlinfo.dli_sname != NULL) { - if (buf != NULL) { - if(!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { + if (dladdr((void*)addr, &dlinfo) != 0) { + // see if we have a matching symbol + if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) { + if (!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname); } + if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; + return true; } - if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; - return true; - } else if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != 0) { - if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), - buf, buflen, offset, dlinfo.dli_fname)) { - return true; + // no matching symbol so try for just file info + if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) { + if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), + buf, buflen, offset, dlinfo.dli_fname)) { + return true; + } } } - if (buf != NULL) buf[0] = '\0'; + buf[0] = '\0'; if (offset != NULL) *offset = -1; return false; } @@ -1764,6 +1771,9 @@ static int address_to_library_name_callback(struct dl_phdr_info *info, bool os::dll_address_to_library_name(address addr, char* buf, int buflen, int* offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + Dl_info dlinfo; struct _address_to_library_name data; @@ -1782,15 +1792,20 @@ bool os::dll_address_to_library_name(address addr, char* buf, // buf already contains library name if (offset) *offset = addr - data.base; return true; - } else if (dladdr((void*)addr, &dlinfo)){ - if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); - if (offset) *offset = addr - (address)dlinfo.dli_fbase; - return true; - } else { - if (buf) buf[0] = '\0'; - if (offset) *offset = -1; - return false; } + if (dladdr((void*)addr, &dlinfo) != 0) { + if (dlinfo.dli_fname != NULL) { + jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); + } + if (dlinfo.dli_fbase != NULL && offset != NULL) { + *offset = addr - (address)dlinfo.dli_fbase; + } + return true; + } + + buf[0] = '\0'; + if (offset) *offset = -1; + return false; } // Loads .dll/.so and @@ -2317,8 +2332,11 @@ void os::jvm_path(char *buf, jint buflen) { bool ret = dll_address_to_library_name( CAST_FROM_FN_PTR(address, os::jvm_path), dli_fname, sizeof(dli_fname), NULL); - assert(ret != 0, "cannot locate libjvm"); - char *rp = realpath(dli_fname, buf); + assert(ret, "cannot locate libjvm"); + char *rp = NULL; + if (ret && dli_fname[0] != '\0') { + rp = realpath(dli_fname, buf); + } if (rp == NULL) return; @@ -4730,20 +4748,20 @@ int os::Linux::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mute bool os::find(address addr, outputStream* st) { Dl_info dlinfo; memset(&dlinfo, 0, sizeof(dlinfo)); - if (dladdr(addr, &dlinfo)) { + if (dladdr(addr, &dlinfo) != 0) { st->print(PTR_FORMAT ": ", addr); - if (dlinfo.dli_sname != NULL) { + if (dlinfo.dli_sname != NULL && dlinfo.dli_saddr != NULL) { st->print("%s+%#x", dlinfo.dli_sname, addr - (intptr_t)dlinfo.dli_saddr); - } else if (dlinfo.dli_fname) { + } else if (dlinfo.dli_fbase != NULL) { st->print("", addr - (intptr_t)dlinfo.dli_fbase); } else { st->print(""); } - if (dlinfo.dli_fname) { + if (dlinfo.dli_fname != NULL) { st->print(" in %s", dlinfo.dli_fname); } - if (dlinfo.dli_fbase) { + if (dlinfo.dli_fbase != NULL) { st->print(" at " PTR_FORMAT, dlinfo.dli_fbase); } st->cr(); @@ -4756,7 +4774,7 @@ bool os::find(address addr, outputStream* st) { if (!lowest) lowest = (address) dlinfo.dli_fbase; if (begin < lowest) begin = lowest; Dl_info dlinfo2; - if (dladdr(end, &dlinfo2) && dlinfo2.dli_saddr != dlinfo.dli_saddr + if (dladdr(end, &dlinfo2) != 0 && dlinfo2.dli_saddr != dlinfo.dli_saddr && end > dlinfo2.dli_saddr && dlinfo2.dli_saddr > begin) end = (address) dlinfo2.dli_saddr; Disassembler::decode(begin, end, st); diff --git a/hotspot/src/os/posix/vm/os_posix.cpp b/hotspot/src/os/posix/vm/os_posix.cpp index e2d7c2d7f7f..bf6a1fafacd 100644 --- a/hotspot/src/os/posix/vm/os_posix.cpp +++ b/hotspot/src/os/posix/vm/os_posix.cpp @@ -259,3 +259,52 @@ const char* os::get_current_directory(char *buf, size_t buflen) { FILE* os::open(int fd, const char* mode) { return ::fdopen(fd, mode); } + +os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { + assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); +} + +/* + * See the caveats for this class in os_posix.hpp + * Protects the callback call so that SIGSEGV / SIGBUS jumps back into this + * method and returns false. If none of the signals are raised, returns true. + * The callback is supposed to provide the method that should be protected. + */ +bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) { + assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread"); + assert(!WatcherThread::watcher_thread()->has_crash_protection(), + "crash_protection already set?"); + + if (sigsetjmp(_jmpbuf, 1) == 0) { + // make sure we can see in the signal handler that we have crash protection + // installed + WatcherThread::watcher_thread()->set_crash_protection(this); + cb.call(); + // and clear the crash protection + WatcherThread::watcher_thread()->set_crash_protection(NULL); + return true; + } + // this happens when we siglongjmp() back + WatcherThread::watcher_thread()->set_crash_protection(NULL); + return false; +} + +void os::WatcherThreadCrashProtection::restore() { + assert(WatcherThread::watcher_thread()->has_crash_protection(), + "must have crash protection"); + + siglongjmp(_jmpbuf, 1); +} + +void os::WatcherThreadCrashProtection::check_crash_protection(int sig, + Thread* thread) { + + if (thread != NULL && + thread->is_Watcher_thread() && + WatcherThread::watcher_thread()->has_crash_protection()) { + + if (sig == SIGSEGV || sig == SIGBUS) { + WatcherThread::watcher_thread()->crash_protection()->restore(); + } + } +} diff --git a/hotspot/src/os/posix/vm/os_posix.hpp b/hotspot/src/os/posix/vm/os_posix.hpp index c7d1d6bd741..16a065acf30 100644 --- a/hotspot/src/os/posix/vm/os_posix.hpp +++ b/hotspot/src/os/posix/vm/os_posix.hpp @@ -37,5 +37,24 @@ protected: }; +/* + * Crash protection for the watcher thread. Wrap the callback + * with a sigsetjmp and in case of a SIGSEGV/SIGBUS we siglongjmp + * back. + * To be able to use this - don't take locks, don't rely on destructors, + * don't make OS library calls, don't allocate memory, don't print, + * don't call code that could leave the heap / memory in an inconsistent state, + * or anything else where we are not in control if we suddenly jump out. + */ +class WatcherThreadCrashProtection : public StackObj { +public: + WatcherThreadCrashProtection(); + bool call(os::CrashProtectionCallback& cb); + + static void check_crash_protection(int signal, Thread* thread); +private: + void restore(); + sigjmp_buf _jmpbuf; +}; #endif diff --git a/hotspot/src/os/solaris/vm/attachListener_solaris.cpp b/hotspot/src/os/solaris/vm/attachListener_solaris.cpp index 9cc66876268..37400795e13 100644 --- a/hotspot/src/os/solaris/vm/attachListener_solaris.cpp +++ b/hotspot/src/os/solaris/vm/attachListener_solaris.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, 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 @@ -576,6 +576,30 @@ AttachOperation* AttachListener::dequeue() { return op; } + +// Performs initialization at vm startup +// For Solaris we remove any stale .java_pid file which could cause +// an attaching process to think we are ready to receive a door_call +// before we are properly initialized + +void AttachListener::vm_start() { + char fn[PATH_MAX+1]; + struct stat64 st; + int ret; + + int n = snprintf(fn, sizeof(fn), "%s/.java_pid%d", + os::get_temp_directory(), os::current_process_id()); + assert(n < sizeof(fn), "java_pid file name buffer overflow"); + + RESTARTABLE(::stat64(fn, &st), ret); + if (ret == 0) { + ret = ::unlink(fn); + if (ret == -1) { + debug_only(warning("failed to remove stale attach pid file at %s", fn)); + } + } +} + int AttachListener::pd_init() { JavaThread* thread = JavaThread::current(); ThreadBlockInVM tbivm(thread); diff --git a/hotspot/src/os/solaris/vm/globals_solaris.hpp b/hotspot/src/os/solaris/vm/globals_solaris.hpp index a567a357e16..2abbe15a7fd 100644 --- a/hotspot/src/os/solaris/vm/globals_solaris.hpp +++ b/hotspot/src/os/solaris/vm/globals_solaris.hpp @@ -30,15 +30,6 @@ // #define RUNTIME_OS_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ \ - product(bool, UseISM, false, \ - "Use Intimate Shared Memory (Solaris Only)") \ - \ - product(bool, UsePermISM, false, \ - "Obsolete flag for compatibility (same as UseISM)") \ - \ - product(bool, UseMPSS, true, \ - "Use Multiple Page Size Support (Solaris 9 Only)") \ - \ product(bool, UseExtendedFileIO, true, \ "Enable workaround for limitations of stdio FILE structure") diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp index ad8c52914be..0a94cb536ab 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.cpp +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp @@ -115,45 +115,6 @@ // for timer info max values which include all bits #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) -#ifdef _GNU_SOURCE -// See bug #6514594 -extern "C" int madvise(caddr_t, size_t, int); -extern "C" int memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg, - int attr, int mask); -#endif //_GNU_SOURCE - -/* - MPSS Changes Start. - The JVM binary needs to be built and run on pre-Solaris 9 - systems, but the constants needed by MPSS are only in Solaris 9 - header files. They are textually replicated here to allow - building on earlier systems. Once building on Solaris 8 is - no longer a requirement, these #defines can be replaced by ordinary - system .h inclusion. - - In earlier versions of the JDK and Solaris, we used ISM for large pages. - But ISM requires shared memory to achieve this and thus has many caveats. - MPSS is a fully transparent and is a cleaner way to get large pages. - Although we still require keeping ISM for backward compatiblitiy as well as - giving the opportunity to use large pages on older systems it is - recommended that MPSS be used for Solaris 9 and above. - -*/ - -#ifndef MC_HAT_ADVISE - -struct memcntl_mha { - uint_t mha_cmd; /* command(s) */ - uint_t mha_flags; - size_t mha_pagesize; -}; -#define MC_HAT_ADVISE 7 /* advise hat map size */ -#define MHA_MAPSIZE_VA 0x1 /* set preferred page size */ -#define MAP_ALIGN 0x200 /* addr specifies alignment */ - -#endif -// MPSS Changes End. - // Here are some liblgrp types from sys/lgrp_user.h to be able to // compile on older systems without this header file. @@ -172,32 +133,6 @@ struct memcntl_mha { # define LGRP_RSRC_MEM 1 /* memory resources */ #endif -// Some more macros from sys/mman.h that are not present in Solaris 8. - -#ifndef MAX_MEMINFO_CNT -/* - * info_req request type definitions for meminfo - * request types starting with MEMINFO_V are used for Virtual addresses - * and should not be mixed with MEMINFO_PLGRP which is targeted for Physical - * addresses - */ -# define MEMINFO_SHIFT 16 -# define MEMINFO_MASK (0xFF << MEMINFO_SHIFT) -# define MEMINFO_VPHYSICAL (0x01 << MEMINFO_SHIFT) /* get physical addr */ -# define MEMINFO_VLGRP (0x02 << MEMINFO_SHIFT) /* get lgroup */ -# define MEMINFO_VPAGESIZE (0x03 << MEMINFO_SHIFT) /* size of phys page */ -# define MEMINFO_VREPLCNT (0x04 << MEMINFO_SHIFT) /* no. of replica */ -# define MEMINFO_VREPL (0x05 << MEMINFO_SHIFT) /* physical replica */ -# define MEMINFO_VREPL_LGRP (0x06 << MEMINFO_SHIFT) /* lgrp of replica */ -# define MEMINFO_PLGRP (0x07 << MEMINFO_SHIFT) /* lgroup for paddr */ - -/* maximum number of addresses meminfo() can process at a time */ -# define MAX_MEMINFO_CNT 256 - -/* maximum number of request types */ -# define MAX_MEMINFO_REQ 31 -#endif - // see thr_setprio(3T) for the basis of these numbers #define MinimumPriority 0 #define NormalPriority 64 @@ -1924,12 +1859,13 @@ bool os::address_is_in_vm(address addr) { Dl_info dlinfo; if (libjvm_base_addr == NULL) { - dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo); - libjvm_base_addr = (address)dlinfo.dli_fbase; + if (dladdr(CAST_FROM_FN_PTR(void *, os::address_is_in_vm), &dlinfo) != 0) { + libjvm_base_addr = (address)dlinfo.dli_fbase; + } assert(libjvm_base_addr !=NULL, "Cannot obtain base address for libjvm"); } - if (dladdr((void *)addr, &dlinfo)) { + if (dladdr((void *)addr, &dlinfo) != 0) { if (libjvm_base_addr == (address)dlinfo.dli_fbase) return true; } @@ -1941,114 +1877,133 @@ static dladdr1_func_type dladdr1_func = NULL; bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int * offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + Dl_info dlinfo; // dladdr1_func was initialized in os::init() - if (dladdr1_func){ - // yes, we have dladdr1 + if (dladdr1_func != NULL) { + // yes, we have dladdr1 - // Support for dladdr1 is checked at runtime; it may be - // available even if the vm is built on a machine that does - // not have dladdr1 support. Make sure there is a value for - // RTLD_DL_SYMENT. - #ifndef RTLD_DL_SYMENT - #define RTLD_DL_SYMENT 1 - #endif + // Support for dladdr1 is checked at runtime; it may be + // available even if the vm is built on a machine that does + // not have dladdr1 support. Make sure there is a value for + // RTLD_DL_SYMENT. + #ifndef RTLD_DL_SYMENT + #define RTLD_DL_SYMENT 1 + #endif #ifdef _LP64 - Elf64_Sym * info; + Elf64_Sym * info; #else - Elf32_Sym * info; + Elf32_Sym * info; #endif - if (dladdr1_func((void *)addr, &dlinfo, (void **)&info, - RTLD_DL_SYMENT)) { - if ((char *)dlinfo.dli_saddr + info->st_size > (char *)addr) { - if (buf != NULL) { - if (!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) - jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname); - } - if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; - return true; - } - } - if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != 0) { - if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), - buf, buflen, offset, dlinfo.dli_fname)) { + if (dladdr1_func((void *)addr, &dlinfo, (void **)&info, + RTLD_DL_SYMENT) != 0) { + // see if we have a matching symbol that covers our address + if (dlinfo.dli_saddr != NULL && + (char *)dlinfo.dli_saddr + info->st_size > (char *)addr) { + if (dlinfo.dli_sname != NULL) { + if (!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { + jio_snprintf(buf, buflen, "%s", dlinfo.dli_sname); + } + if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; return true; } } - if (buf != NULL) buf[0] = '\0'; - if (offset != NULL) *offset = -1; - return false; - } else { - // no, only dladdr is available - if (dladdr((void *)addr, &dlinfo)) { - if (buf != NULL) { - if (!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) - jio_snprintf(buf, buflen, dlinfo.dli_sname); - } - if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; - return true; - } else if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != 0) { + // no matching symbol so try for just file info + if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) { if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), - buf, buflen, offset, dlinfo.dli_fname)) { + buf, buflen, offset, dlinfo.dli_fname)) { return true; } } - if (buf != NULL) buf[0] = '\0'; - if (offset != NULL) *offset = -1; - return false; + } + buf[0] = '\0'; + if (offset != NULL) *offset = -1; + return false; } + + // no, only dladdr is available + if (dladdr((void *)addr, &dlinfo) != 0) { + // see if we have a matching symbol + if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) { + if (!Decoder::demangle(dlinfo.dli_sname, buf, buflen)) { + jio_snprintf(buf, buflen, dlinfo.dli_sname); + } + if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; + return true; + } + // no matching symbol so try for just file info + if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) { + if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), + buf, buflen, offset, dlinfo.dli_fname)) { + return true; + } + } + } + buf[0] = '\0'; + if (offset != NULL) *offset = -1; + return false; } bool os::dll_address_to_library_name(address addr, char* buf, int buflen, int* offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + Dl_info dlinfo; - if (dladdr((void*)addr, &dlinfo)){ - if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); - if (offset) *offset = addr - (address)dlinfo.dli_fbase; - return true; - } else { - if (buf) buf[0] = '\0'; - if (offset) *offset = -1; - return false; + if (dladdr((void*)addr, &dlinfo) != 0) { + if (dlinfo.dli_fname != NULL) { + jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); + } + if (dlinfo.dli_fbase != NULL && offset != NULL) { + *offset = addr - (address)dlinfo.dli_fbase; + } + return true; } + + buf[0] = '\0'; + if (offset) *offset = -1; + return false; } // Prints the names and full paths of all opened dynamic libraries // for current process void os::print_dll_info(outputStream * st) { - Dl_info dli; - void *handle; - Link_map *map; - Link_map *p; + Dl_info dli; + void *handle; + Link_map *map; + Link_map *p; - st->print_cr("Dynamic libraries:"); st->flush(); + st->print_cr("Dynamic libraries:"); st->flush(); - if (!dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli)) { - st->print_cr("Error: Cannot print dynamic libraries."); - return; - } - handle = dlopen(dli.dli_fname, RTLD_LAZY); - if (handle == NULL) { - st->print_cr("Error: Cannot print dynamic libraries."); - return; - } - dlinfo(handle, RTLD_DI_LINKMAP, &map); - if (map == NULL) { - st->print_cr("Error: Cannot print dynamic libraries."); - return; - } + if (dladdr(CAST_FROM_FN_PTR(void *, os::print_dll_info), &dli) == 0 || + dli.dli_fname == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + handle = dlopen(dli.dli_fname, RTLD_LAZY); + if (handle == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } + dlinfo(handle, RTLD_DI_LINKMAP, &map); + if (map == NULL) { + st->print_cr("Error: Cannot print dynamic libraries."); + return; + } - while (map->l_prev != NULL) - map = map->l_prev; + while (map->l_prev != NULL) + map = map->l_prev; - while (map != NULL) { - st->print_cr(PTR_FORMAT " \t%s", map->l_addr, map->l_name); - map = map->l_next; - } + while (map != NULL) { + st->print_cr(PTR_FORMAT " \t%s", map->l_addr, map->l_name); + map = map->l_next; + } - dlclose(handle); + dlclose(handle); } // Loads .dll/.so and @@ -2475,7 +2430,12 @@ void os::jvm_path(char *buf, jint buflen) { Dl_info dlinfo; int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo); assert(ret != 0, "cannot locate libjvm"); - realpath((char *)dlinfo.dli_fname, buf); + if (ret != 0 && dlinfo.dli_fname != NULL) { + realpath((char *)dlinfo.dli_fname, buf); + } else { + buf[0] = '\0'; + return; + } if (Arguments::created_by_gamma_launcher()) { // Support for the gamma launcher. Typical value for buf is @@ -2859,7 +2819,7 @@ int os::Solaris::commit_memory_impl(char* addr, size_t bytes, size_t alignment_hint, bool exec) { int err = Solaris::commit_memory_impl(addr, bytes, exec); if (err == 0) { - if (UseMPSS && alignment_hint > (size_t)vm_page_size()) { + if (UseLargePages && (alignment_hint > (size_t)vm_page_size())) { // If the large page size has been set and the VM // is using large pages, use the large page size // if it is smaller than the alignment hint. This is @@ -2878,7 +2838,7 @@ int os::Solaris::commit_memory_impl(char* addr, size_t bytes, page_size = alignment_hint; } // Since this is a hint, ignore any failures. - (void)Solaris::set_mpss_range(addr, bytes, page_size); + (void)Solaris::setup_large_pages(addr, bytes, page_size); } } return err; @@ -2921,8 +2881,8 @@ bool os::remove_stack_guard_pages(char* addr, size_t size) { void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) { assert((intptr_t)addr % alignment_hint == 0, "Address should be aligned."); assert((intptr_t)(addr + bytes) % alignment_hint == 0, "End should be aligned."); - if (UseLargePages && UseMPSS) { - Solaris::set_mpss_range(addr, bytes, alignment_hint); + if (UseLargePages) { + Solaris::setup_large_pages(addr, bytes, alignment_hint); } } @@ -3321,47 +3281,8 @@ bool os::unguard_memory(char* addr, size_t bytes) { } // Large page support - -// UseLargePages is the master flag to enable/disable large page memory. -// UseMPSS and UseISM are supported for compatibility reasons. Their combined -// effects can be described in the following table: -// -// UseLargePages UseMPSS UseISM -// false * * => UseLargePages is the master switch, turning -// it off will turn off both UseMPSS and -// UseISM. VM will not use large page memory -// regardless the settings of UseMPSS/UseISM. -// true false false => Unless future Solaris provides other -// mechanism to use large page memory, this -// combination is equivalent to -UseLargePages, -// VM will not use large page memory -// true true false => JVM will use MPSS for large page memory. -// This is the default behavior. -// true false true => JVM will use ISM for large page memory. -// true true true => JVM will use ISM if it is available. -// Otherwise, JVM will fall back to MPSS. -// Becaues ISM is now available on all -// supported Solaris versions, this combination -// is equivalent to +UseISM -UseMPSS. - static size_t _large_page_size = 0; -bool os::Solaris::ism_sanity_check(bool warn, size_t * page_size) { - // x86 uses either 2M or 4M page, depending on whether PAE (Physical Address - // Extensions) mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. Sparc - // can support multiple page sizes. - - // Don't bother to probe page size because getpagesizes() comes with MPSS. - // ISM is only recommended on old Solaris where there is no MPSS support. - // Simply choose a conservative value as default. - *page_size = LargePageSizeInBytes ? LargePageSizeInBytes : - SPARC_ONLY(4 * M) IA32_ONLY(4 * M) AMD64_ONLY(2 * M) - ARM_ONLY(2 * M); - - // ISM is available on all supported Solaris versions - return true; -} - // Insertion sort for small arrays (descending order). static void insertion_sort_descending(size_t* array, int len) { for (int i = 0; i < len; i++) { @@ -3374,7 +3295,7 @@ static void insertion_sort_descending(size_t* array, int len) { } } -bool os::Solaris::mpss_sanity_check(bool warn, size_t * page_size) { +bool os::Solaris::mpss_sanity_check(bool warn, size_t* page_size) { const unsigned int usable_count = VM_Version::page_size_count(); if (usable_count == 1) { return false; @@ -3440,41 +3361,24 @@ bool os::Solaris::mpss_sanity_check(bool warn, size_t * page_size) { } void os::large_page_init() { - if (!UseLargePages) { - UseISM = false; - UseMPSS = false; - return; + if (UseLargePages) { + // print a warning if any large page related flag is specified on command line + bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || + !FLAG_IS_DEFAULT(LargePageSizeInBytes); + + UseLargePages = Solaris::mpss_sanity_check(warn_on_failure, &_large_page_size); } - - // print a warning if any large page related flag is specified on command line - bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) || - !FLAG_IS_DEFAULT(UseISM) || - !FLAG_IS_DEFAULT(UseMPSS) || - !FLAG_IS_DEFAULT(LargePageSizeInBytes); - UseISM = UseISM && - Solaris::ism_sanity_check(warn_on_failure, &_large_page_size); - if (UseISM) { - // ISM disables MPSS to be compatible with old JDK behavior - UseMPSS = false; - _page_sizes[0] = _large_page_size; - _page_sizes[1] = vm_page_size(); - } - - UseMPSS = UseMPSS && - Solaris::mpss_sanity_check(warn_on_failure, &_large_page_size); - - UseLargePages = UseISM || UseMPSS; } -bool os::Solaris::set_mpss_range(caddr_t start, size_t bytes, size_t align) { +bool os::Solaris::setup_large_pages(caddr_t start, size_t bytes, size_t align) { // Signal to OS that we want large pages for addresses // from addr, addr + bytes struct memcntl_mha mpss_struct; mpss_struct.mha_cmd = MHA_MAPSIZE_VA; mpss_struct.mha_pagesize = align; mpss_struct.mha_flags = 0; - if (memcntl(start, bytes, MC_HAT_ADVISE, - (caddr_t) &mpss_struct, 0, 0) < 0) { + // Upon successful completion, memcntl() returns 0 + if (memcntl(start, bytes, MC_HAT_ADVISE, (caddr_t) &mpss_struct, 0, 0)) { debug_only(warning("Attempt to use MPSS failed.")); return false; } @@ -3482,72 +3386,13 @@ bool os::Solaris::set_mpss_range(caddr_t start, size_t bytes, size_t align) { } char* os::reserve_memory_special(size_t size, char* addr, bool exec) { - // "exec" is passed in but not used. Creating the shared image for - // the code cache doesn't have an SHM_X executable permission to check. - assert(UseLargePages && UseISM, "only for ISM large pages"); - - char* retAddr = NULL; - int shmid; - key_t ismKey; - - bool warn_on_failure = UseISM && - (!FLAG_IS_DEFAULT(UseLargePages) || - !FLAG_IS_DEFAULT(UseISM) || - !FLAG_IS_DEFAULT(LargePageSizeInBytes) - ); - char msg[128]; - - ismKey = IPC_PRIVATE; - - // Create a large shared memory region to attach to based on size. - // Currently, size is the total size of the heap - shmid = shmget(ismKey, size, SHM_R | SHM_W | IPC_CREAT); - if (shmid == -1){ - if (warn_on_failure) { - jio_snprintf(msg, sizeof(msg), "Failed to reserve shared memory (errno = %d).", errno); - warning(msg); - } - return NULL; - } - - // Attach to the region - retAddr = (char *) shmat(shmid, 0, SHM_SHARE_MMU | SHM_R | SHM_W); - int err = errno; - - // Remove shmid. If shmat() is successful, the actual shared memory segment - // will be deleted when it's detached by shmdt() or when the process - // terminates. If shmat() is not successful this will remove the shared - // segment immediately. - shmctl(shmid, IPC_RMID, NULL); - - if (retAddr == (char *) -1) { - if (warn_on_failure) { - jio_snprintf(msg, sizeof(msg), "Failed to attach shared memory (errno = %d).", err); - warning(msg); - } - return NULL; - } - if ((retAddr != NULL) && UseNUMAInterleaving) { - numa_make_global(retAddr, size); - } - - // The memory is committed - MemTracker::record_virtual_memory_reserve_and_commit((address)retAddr, size, mtNone, CURRENT_PC); - - return retAddr; + fatal("os::reserve_memory_special should not be called on Solaris."); + return NULL; } bool os::release_memory_special(char* base, size_t bytes) { - MemTracker::Tracker tkr = MemTracker::get_virtual_memory_release_tracker(); - // detaching the SHM segment will also delete it, see reserve_memory_special() - int rslt = shmdt(base); - if (rslt == 0) { - tkr.record((address)base, bytes); - return true; - } else { - tkr.discard(); - return false; - } + fatal("os::release_memory_special should not be called on Solaris."); + return false; } size_t os::large_page_size() { @@ -3557,11 +3402,11 @@ size_t os::large_page_size() { // MPSS allows application to commit large page memory on demand; with ISM // the entire memory region must be allocated as shared memory. bool os::can_commit_large_page_memory() { - return UseISM ? false : true; + return true; } bool os::can_execute_large_page_memory() { - return UseISM ? false : true; + return true; } static int os_sleep(jlong millis, bool interruptible) { @@ -3835,28 +3680,6 @@ static bool priocntl_enable = false; static const int criticalPrio = 60; // FX/60 is critical thread class/priority on T4 static int java_MaxPriority_to_os_priority = 0; // Saved mapping -// Call the version of priocntl suitable for all supported versions -// of Solaris. We need to call through this wrapper so that we can -// build on Solaris 9 and run on Solaris 8, 9 and 10. -// -// This code should be removed if we ever stop supporting Solaris 8 -// and earlier releases. - -static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg); -typedef long (*priocntl_type)(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg); -static priocntl_type priocntl_ptr = priocntl_stub; - -// Stub to set the value of the real pointer, and then call the real -// function. - -static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t arg) { - // Try Solaris 8- name only. - priocntl_type tmp = (priocntl_type)dlsym(RTLD_DEFAULT, "__priocntl"); - guarantee(tmp != NULL, "priocntl function not found."); - priocntl_ptr = tmp; - return (*priocntl_ptr)(PC_VERSION, idtype, id, cmd, arg); -} - // lwp_priocntl_init // @@ -3864,9 +3687,7 @@ static long priocntl_stub(int pcver, idtype_t idtype, id_t id, int cmd, caddr_t // // Return errno or 0 if OK. // -static -int lwp_priocntl_init () -{ +static int lwp_priocntl_init () { int rslt; pcinfo_t ClassInfo; pcparms_t ParmInfo; @@ -3906,7 +3727,7 @@ int lwp_priocntl_init () strcpy(ClassInfo.pc_clname, "TS"); ClassInfo.pc_cid = -1; - rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); if (rslt < 0) return errno; assert(ClassInfo.pc_cid != -1, "cid for TS class is -1"); tsLimits.schedPolicy = ClassInfo.pc_cid; @@ -3915,7 +3736,7 @@ int lwp_priocntl_init () strcpy(ClassInfo.pc_clname, "IA"); ClassInfo.pc_cid = -1; - rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); if (rslt < 0) return errno; assert(ClassInfo.pc_cid != -1, "cid for IA class is -1"); iaLimits.schedPolicy = ClassInfo.pc_cid; @@ -3924,7 +3745,7 @@ int lwp_priocntl_init () strcpy(ClassInfo.pc_clname, "RT"); ClassInfo.pc_cid = -1; - rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); if (rslt < 0) return errno; assert(ClassInfo.pc_cid != -1, "cid for RT class is -1"); rtLimits.schedPolicy = ClassInfo.pc_cid; @@ -3933,7 +3754,7 @@ int lwp_priocntl_init () strcpy(ClassInfo.pc_clname, "FX"); ClassInfo.pc_cid = -1; - rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + rslt = priocntl(P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); if (rslt < 0) return errno; assert(ClassInfo.pc_cid != -1, "cid for FX class is -1"); fxLimits.schedPolicy = ClassInfo.pc_cid; @@ -3944,7 +3765,7 @@ int lwp_priocntl_init () // This will normally be IA, TS or, rarely, FX or RT. memset(&ParmInfo, 0, sizeof(ParmInfo)); ParmInfo.pc_cid = PC_CLNULL; - rslt = (*priocntl_ptr) (PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo); + rslt = priocntl(P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo); if (rslt < 0) return errno; myClass = ParmInfo.pc_cid; @@ -3952,7 +3773,7 @@ int lwp_priocntl_init () // about the class. ClassInfo.pc_cid = myClass; ClassInfo.pc_clname[0] = 0; - rslt = (*priocntl_ptr) (PC_VERSION, (idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo); + rslt = priocntl((idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo); if (rslt < 0) return errno; if (ThreadPriorityVerbose) { @@ -3961,7 +3782,7 @@ int lwp_priocntl_init () memset(&ParmInfo, 0, sizeof(pcparms_t)); ParmInfo.pc_cid = PC_CLNULL; - rslt = (*priocntl_ptr)(PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo); + rslt = priocntl(P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo); if (rslt < 0) return errno; if (ParmInfo.pc_cid == rtLimits.schedPolicy) { @@ -4065,7 +3886,7 @@ int set_lwp_class_and_priority(int ThreadID, int lwpid, memset(&ParmInfo, 0, sizeof(pcparms_t)); ParmInfo.pc_cid = PC_CLNULL; - rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ParmInfo); + rslt = priocntl(P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ParmInfo); if (rslt < 0) return errno; int cur_class = ParmInfo.pc_cid; @@ -4133,7 +3954,7 @@ int set_lwp_class_and_priority(int ThreadID, int lwpid, return EINVAL; // no clue, punt } - rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_SETPARMS, (caddr_t)&ParmInfo); + rslt = priocntl(P_LWPID, lwpid, PC_SETPARMS, (caddr_t)&ParmInfo); if (ThreadPriorityVerbose && rslt) { tty->print_cr ("PC_SETPARMS ->%d %d\n", rslt, errno); } @@ -4152,7 +3973,7 @@ int set_lwp_class_and_priority(int ThreadID, int lwpid, memset(&ReadBack, 0, sizeof(pcparms_t)); ReadBack.pc_cid = PC_CLNULL; - rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ReadBack); + rslt = priocntl(P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ReadBack); assert(rslt >= 0, "priocntl failed"); Actual = Expected = 0xBAD; assert(ParmInfo.pc_cid == ReadBack.pc_cid, "cid's don't match"); @@ -5244,11 +5065,6 @@ uint_t os::Solaris::getisax(uint32_t* array, uint_t n) { return _getisax(array, n); } -// Symbol doesn't exist in Solaris 8 pset.h -#ifndef PS_MYID -#define PS_MYID -3 -#endif - // int pset_getloadavg(psetid_t pset, double loadavg[], int nelem); typedef long (*pset_getloadavg_type)(psetid_t pset, double loadavg[], int nelem); static pset_getloadavg_type pset_getloadavg_ptr = NULL; @@ -5418,20 +5234,6 @@ jint os::init_2(void) { UseNUMA = false; } } - // ISM is not compatible with the NUMA allocator - it always allocates - // pages round-robin across the lgroups. - if (UseNUMA && UseLargePages && UseISM) { - if (!FLAG_IS_DEFAULT(UseNUMA)) { - if (FLAG_IS_DEFAULT(UseLargePages) && FLAG_IS_DEFAULT(UseISM)) { - UseLargePages = false; - } else { - warning("UseNUMA is not compatible with ISM large pages, disabling NUMA allocator"); - UseNUMA = false; - } - } else { - UseNUMA = false; - } - } if (!UseNUMA && ForceNUMA) { UseNUMA = true; } @@ -6077,24 +5879,20 @@ int os::loadavg(double loadavg[], int nelem) { bool os::find(address addr, outputStream* st) { Dl_info dlinfo; memset(&dlinfo, 0, sizeof(dlinfo)); - if (dladdr(addr, &dlinfo)) { -#ifdef _LP64 - st->print("0x%016lx: ", addr); -#else - st->print("0x%08x: ", addr); -#endif - if (dlinfo.dli_sname != NULL) + if (dladdr(addr, &dlinfo) != 0) { + st->print(PTR_FORMAT ": ", addr); + if (dlinfo.dli_sname != NULL && dlinfo.dli_saddr != NULL) { st->print("%s+%#lx", dlinfo.dli_sname, addr-(intptr_t)dlinfo.dli_saddr); - else if (dlinfo.dli_fname) + } else if (dlinfo.dli_fbase != NULL) st->print("", addr-(intptr_t)dlinfo.dli_fbase); else st->print(""); - if (dlinfo.dli_fname) st->print(" in %s", dlinfo.dli_fname); -#ifdef _LP64 - if (dlinfo.dli_fbase) st->print(" at 0x%016lx", dlinfo.dli_fbase); -#else - if (dlinfo.dli_fbase) st->print(" at 0x%08x", dlinfo.dli_fbase); -#endif + if (dlinfo.dli_fname != NULL) { + st->print(" in %s", dlinfo.dli_fname); + } + if (dlinfo.dli_fbase != NULL) { + st->print(" at " PTR_FORMAT, dlinfo.dli_fbase); + } st->cr(); if (Verbose) { @@ -6105,7 +5903,7 @@ bool os::find(address addr, outputStream* st) { if (!lowest) lowest = (address) dlinfo.dli_fbase; if (begin < lowest) begin = lowest; Dl_info dlinfo2; - if (dladdr(end, &dlinfo2) && dlinfo2.dli_saddr != dlinfo.dli_saddr + if (dladdr(end, &dlinfo2) != 0 && dlinfo2.dli_saddr != dlinfo.dli_saddr && end > dlinfo2.dli_saddr && dlinfo2.dli_saddr > begin) end = (address) dlinfo2.dli_saddr; Disassembler::decode(begin, end, st); diff --git a/hotspot/src/os/solaris/vm/os_solaris.hpp b/hotspot/src/os/solaris/vm/os_solaris.hpp index f7f0a3d7283..330d4fbc591 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.hpp +++ b/hotspot/src/os/solaris/vm/os_solaris.hpp @@ -106,8 +106,8 @@ class Solaris { static meminfo_func_t _meminfo; - // Large Page Support--mpss. - static bool set_mpss_range(caddr_t start, size_t bytes, size_t align); + // Large Page Support + static bool setup_large_pages(caddr_t start, size_t bytes, size_t align); static void init_thread_fpu_state(void); @@ -174,7 +174,6 @@ class Solaris { static char* mmap_chunk(char *addr, size_t size, int flags, int prot); static char* anon_mmap(char* requested_addr, size_t bytes, size_t alignment_hint, bool fixed); static bool mpss_sanity_check(bool warn, size_t * page_size); - static bool ism_sanity_check (bool warn, size_t * page_size); // Workaround for 4352906. thr_stksegment sometimes returns // a bad value for the primordial thread's stack base when diff --git a/hotspot/src/os/windows/vm/attachListener_windows.cpp b/hotspot/src/os/windows/vm/attachListener_windows.cpp index 1d5857f1c2f..34a454f6363 100644 --- a/hotspot/src/os/windows/vm/attachListener_windows.cpp +++ b/hotspot/src/os/windows/vm/attachListener_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, 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 @@ -358,6 +358,10 @@ AttachOperation* AttachListener::dequeue() { return op; } +void AttachListener::vm_start() { + // nothing to do +} + int AttachListener::pd_init() { return Win32AttachListener::init(); } diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index cc43934d423..3b44d9a220f 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -1420,34 +1420,40 @@ static int _locate_module_by_addr(int pid, char * mod_fname, address base_addr, bool os::dll_address_to_library_name(address addr, char* buf, int buflen, int* offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + // NOTE: the reason we don't use SymGetModuleInfo() is it doesn't always // return the full path to the DLL file, sometimes it returns path // to the corresponding PDB file (debug info); sometimes it only // returns partial path, which makes life painful. - struct _modinfo mi; - mi.addr = addr; - mi.full_path = buf; - mi.buflen = buflen; - int pid = os::current_process_id(); - if (enumerate_modules(pid, _locate_module_by_addr, (void *)&mi)) { - // buf already contains path name - if (offset) *offset = addr - mi.base_addr; - return true; - } else { - if (buf) buf[0] = '\0'; - if (offset) *offset = -1; - return false; - } + struct _modinfo mi; + mi.addr = addr; + mi.full_path = buf; + mi.buflen = buflen; + int pid = os::current_process_id(); + if (enumerate_modules(pid, _locate_module_by_addr, (void *)&mi)) { + // buf already contains path name + if (offset) *offset = addr - mi.base_addr; + return true; + } + + buf[0] = '\0'; + if (offset) *offset = -1; + return false; } bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset) { + // buf is not optional, but offset is optional + assert(buf != NULL, "sanity check"); + if (Decoder::decode(addr, buf, buflen, offset)) { return true; } if (offset != NULL) *offset = -1; - if (buf != NULL) buf[0] = '\0'; + buf[0] = '\0'; return false; } @@ -2317,6 +2323,11 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo) { #endif Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady + // Handle SafeFetch32 and SafeFetchN exceptions. + if (StubRoutines::is_safefetch_fault(pc)) { + return Handle_Exception(exceptionInfo, StubRoutines::continuation_for_safefetch_fault(pc)); + } + #ifndef _WIN64 // Execution protection violation - win32 running on AMD64 only // Handled first to avoid misdiagnosis as a "normal" access violation; @@ -2689,6 +2700,19 @@ address os::win32::fast_jni_accessor_wrapper(BasicType type) { } #endif +#ifndef PRODUCT +void os::win32::call_test_func_with_wrapper(void (*funcPtr)(void)) { + // Install a win32 structured exception handler around the test + // function call so the VM can generate an error dump if needed. + __try { + (*funcPtr)(); + } __except(topLevelExceptionFilter( + (_EXCEPTION_POINTERS*)_exception_info())) { + // Nothing to do. + } +} +#endif + // Virtual Memory int os::vm_page_size() { return os::win32::vm_page_size(); } @@ -4665,6 +4689,34 @@ void os::pause() { } } +os::WatcherThreadCrashProtection::WatcherThreadCrashProtection() { + assert(Thread::current()->is_Watcher_thread(), "Must be WatcherThread"); +} + +/* + * See the caveats for this class in os_windows.hpp + * Protects the callback call so that raised OS EXCEPTIONS causes a jump back + * into this method and returns false. If no OS EXCEPTION was raised, returns + * true. + * The callback is supposed to provide the method that should be protected. + */ +bool os::WatcherThreadCrashProtection::call(os::CrashProtectionCallback& cb) { + assert(Thread::current()->is_Watcher_thread(), "Only for WatcherThread"); + assert(!WatcherThread::watcher_thread()->has_crash_protection(), + "crash_protection already set?"); + + bool success = true; + __try { + WatcherThread::watcher_thread()->set_crash_protection(this); + cb.call(); + } __except(EXCEPTION_EXECUTE_HANDLER) { + // only for protection, nothing to do + success = false; + } + WatcherThread::watcher_thread()->set_crash_protection(NULL); + return success; +} + // An Event wraps a win32 "CreateEvent" kernel handle. // // We have a number of choices regarding "CreateEvent" win32 handle leakage: diff --git a/hotspot/src/os/windows/vm/os_windows.hpp b/hotspot/src/os/windows/vm/os_windows.hpp index f3196af9afc..67ed8a4af7c 100644 --- a/hotspot/src/os/windows/vm/os_windows.hpp +++ b/hotspot/src/os/windows/vm/os_windows.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -94,10 +94,28 @@ class win32 { static address fast_jni_accessor_wrapper(BasicType); #endif +#ifndef PRODUCT + static void call_test_func_with_wrapper(void (*funcPtr)(void)); +#endif + // filter function to ignore faults on serializations page static LONG WINAPI serialize_fault_filter(struct _EXCEPTION_POINTERS* e); }; +/* + * Crash protection for the watcher thread. Wrap the callback + * with a __try { call() } + * To be able to use this - don't take locks, don't rely on destructors, + * don't make OS library calls, don't allocate memory, don't print, + * don't call code that could leave the heap / memory in an inconsistent state, + * or anything else where we are not in control if we suddenly jump out. + */ +class WatcherThreadCrashProtection : public StackObj { +public: + WatcherThreadCrashProtection(); + bool call(os::CrashProtectionCallback& cb); +}; + class PlatformEvent : public CHeapObj { private: double CachePad [4] ; // increase odds that _Event is sole occupant of cache line diff --git a/hotspot/src/os/windows/vm/os_windows.inline.hpp b/hotspot/src/os/windows/vm/os_windows.inline.hpp index 5b1c46dfc59..5303743aee4 100644 --- a/hotspot/src/os/windows/vm/os_windows.inline.hpp +++ b/hotspot/src/os/windows/vm/os_windows.inline.hpp @@ -106,4 +106,10 @@ inline size_t os::write(int fd, const void *buf, unsigned int nBytes) { inline int os::close(int fd) { return ::close(fd); } + +#ifndef PRODUCT + #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) \ + os::win32::call_test_func_with_wrapper(f) +#endif + #endif // OS_WINDOWS_VM_OS_WINDOWS_INLINE_HPP diff --git a/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_32.s b/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_32.s index 402c8da11a6..3275996f0c7 100644 --- a/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_32.s +++ b/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_32.s @@ -63,24 +63,6 @@ SYMBOL(fixcw): popl %eax ret - .globl SYMBOL(SafeFetch32), SYMBOL(Fetch32PFI), SYMBOL(Fetch32Resume) - .globl SYMBOL(SafeFetchN) - ## TODO: avoid exposing Fetch32PFI and Fetch32Resume. - ## Instead, the signal handler would call a new SafeFetchTriage(FaultingEIP) - ## routine to vet the address. If the address is the faulting LD then - ## SafeFetchTriage() would return the resume-at EIP, otherwise null. - ELF_TYPE(SafeFetch32,@function) - .p2align 4,,15 -SYMBOL(SafeFetch32): -SYMBOL(SafeFetchN): - movl 0x8(%esp), %eax - movl 0x4(%esp), %ecx -SYMBOL(Fetch32PFI): - movl (%ecx), %eax -SYMBOL(Fetch32Resume): - ret - - .globl SYMBOL(SpinPause) ELF_TYPE(SpinPause,@function) .p2align 4,,15 diff --git a/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_64.s b/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_64.s index 65d2db45f70..2f70fce77a3 100644 --- a/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_64.s +++ b/hotspot/src/os_cpu/bsd_x86/vm/bsd_x86_64.s @@ -46,28 +46,6 @@ .text - .globl SYMBOL(SafeFetch32), SYMBOL(Fetch32PFI), SYMBOL(Fetch32Resume) - .p2align 4,,15 - ELF_TYPE(SafeFetch32,@function) - // Prototype: int SafeFetch32 (int * Adr, int ErrValue) -SYMBOL(SafeFetch32): - movl %esi, %eax -SYMBOL(Fetch32PFI): - movl (%rdi), %eax -SYMBOL(Fetch32Resume): - ret - - .globl SYMBOL(SafeFetchN), SYMBOL(FetchNPFI), SYMBOL(FetchNResume) - .p2align 4,,15 - ELF_TYPE(SafeFetchN,@function) - // Prototype: intptr_t SafeFetchN (intptr_t * Adr, intptr_t ErrValue) -SYMBOL(SafeFetchN): - movq %rsi, %rax -SYMBOL(FetchNPFI): - movq (%rdi), %rax -SYMBOL(FetchNResume): - ret - .globl SYMBOL(SpinPause) .p2align 4,,15 ELF_TYPE(SpinPause,@function) diff --git a/hotspot/src/os_cpu/bsd_x86/vm/orderAccess_bsd_x86.inline.hpp b/hotspot/src/os_cpu/bsd_x86/vm/orderAccess_bsd_x86.inline.hpp index 0ac6d3093f8..8cb9d4a1451 100644 --- a/hotspot/src/os_cpu/bsd_x86/vm/orderAccess_bsd_x86.inline.hpp +++ b/hotspot/src/os_cpu/bsd_x86/vm/orderAccess_bsd_x86.inline.hpp @@ -72,7 +72,7 @@ inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } inline julong OrderAccess::load_acquire(volatile julong* p) { return Atomic::load((volatile jlong*)p); } inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } -inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return jdouble_cast(Atomic::load((volatile jlong*)p)); } inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } @@ -87,7 +87,7 @@ inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } inline void OrderAccess::release_store(volatile julong* p, julong v) { Atomic::store((jlong)v, (volatile jlong*)p); } inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } -inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { release_store((volatile jlong*)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } @@ -190,7 +190,7 @@ inline void OrderAccess::release_store_fence(volatile juint* p, juint v) inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store_fence((volatile jlong*)p, (jlong)v); } inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } -inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store_fence((volatile jlong*)p, jdouble_cast(v)); } inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { #ifdef AMD64 diff --git a/hotspot/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp b/hotspot/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp index aa36599ea2a..24f364e985f 100644 --- a/hotspot/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp +++ b/hotspot/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp @@ -385,13 +385,6 @@ enum { trap_page_fault = 0xE }; -extern "C" void Fetch32PFI () ; -extern "C" void Fetch32Resume () ; -#ifdef AMD64 -extern "C" void FetchNPFI () ; -extern "C" void FetchNResume () ; -#endif // AMD64 - extern "C" JNIEXPORT int JVM_handle_bsd_signal(int sig, siginfo_t* info, @@ -401,6 +394,10 @@ JVM_handle_bsd_signal(int sig, Thread* t = ThreadLocalStorage::get_thread_slow(); + // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away + // (no destructors can be run) + os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + SignalHandlerMark shm(t); // Note: it's not uncommon that JNI code uses signal/sigset to install @@ -454,16 +451,10 @@ JVM_handle_bsd_signal(int sig, if (info != NULL && uc != NULL && thread != NULL) { pc = (address) os::Bsd::ucontext_get_pc(uc); - if (pc == (address) Fetch32PFI) { - uc->context_pc = intptr_t(Fetch32Resume) ; - return 1 ; + if (StubRoutines::is_safefetch_fault(pc)) { + uc->context_pc = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + return 1; } -#ifdef AMD64 - if (pc == (address) FetchNPFI) { - uc->context_pc = intptr_t (FetchNResume) ; - return 1 ; - } -#endif // AMD64 // Handle ALL stack overflow variations here if (sig == SIGSEGV || sig == SIGBUS) { diff --git a/hotspot/src/os_cpu/linux_sparc/vm/linux_sparc.s b/hotspot/src/os_cpu/linux_sparc/vm/linux_sparc.s index e04f871f49e..d7c2ce87414 100644 --- a/hotspot/src/os_cpu/linux_sparc/vm/linux_sparc.s +++ b/hotspot/src/os_cpu/linux_sparc/vm/linux_sparc.s @@ -21,42 +21,6 @@ # questions. # - # Prototype: int SafeFetch32 (int * adr, int ErrValue) - # The "ld" at Fetch32 is potentially faulting instruction. - # If the instruction traps the trap handler will arrange - # for control to resume at Fetch32Resume. - # By convention with the trap handler we ensure there is a non-CTI - # instruction in the trap shadow. - - - .globl SafeFetch32, Fetch32PFI, Fetch32Resume - .globl SafeFetchN - .align 32 - .type SafeFetch32,@function -SafeFetch32: - mov %o0, %g1 - mov %o1, %o0 -Fetch32PFI: - # <-- Potentially faulting instruction - ld [%g1], %o0 -Fetch32Resume: - nop - retl - nop - - .globl SafeFetchN, FetchNPFI, FetchNResume - .type SafeFetchN,@function - .align 32 -SafeFetchN: - mov %o0, %g1 - mov %o1, %o0 -FetchNPFI: - ldn [%g1], %o0 -FetchNResume: - nop - retl - nop - # Possibilities: # -- membar # -- CAS (SP + BIAS, G0, G0) diff --git a/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp b/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp index d97f0e041bf..e7879bee690 100644 --- a/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp +++ b/hotspot/src/os_cpu/linux_sparc/vm/os_linux_sparc.cpp @@ -366,18 +366,9 @@ intptr_t* os::Linux::ucontext_get_fp(ucontext_t *uc) { // Utility functions -extern "C" void Fetch32PFI(); -extern "C" void Fetch32Resume(); -extern "C" void FetchNPFI(); -extern "C" void FetchNResume(); - inline static bool checkPrefetch(sigcontext* uc, address pc) { - if (pc == (address) Fetch32PFI) { - set_cont_address(uc, address(Fetch32Resume)); - return true; - } - if (pc == (address) FetchNPFI) { - set_cont_address(uc, address(FetchNResume)); + if (StubRoutines::is_safefetch_fault(pc)) { + set_cont_address(uc, address(StubRoutines::continuation_for_safefetch_fault(pc))); return true; } return false; @@ -553,6 +544,10 @@ JVM_handle_linux_signal(int sig, Thread* t = ThreadLocalStorage::get_thread_slow(); + // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away + // (no destructors can be run) + os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + SignalHandlerMark shm(t); // Note: it's not uncommon that JNI code uses signal/sigset to install diff --git a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s index d29d31df464..7936cbf52bd 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s +++ b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_32.s @@ -42,24 +42,6 @@ .text - .globl SafeFetch32, Fetch32PFI, Fetch32Resume - .globl SafeFetchN - ## TODO: avoid exposing Fetch32PFI and Fetch32Resume. - ## Instead, the signal handler would call a new SafeFetchTriage(FaultingEIP) - ## routine to vet the address. If the address is the faulting LD then - ## SafeFetchTriage() would return the resume-at EIP, otherwise null. - .type SafeFetch32,@function - .p2align 4,,15 -SafeFetch32: -SafeFetchN: - movl 0x8(%esp), %eax - movl 0x4(%esp), %ecx -Fetch32PFI: - movl (%ecx), %eax -Fetch32Resume: - ret - - .globl SpinPause .type SpinPause,@function .p2align 4,,15 diff --git a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s index 8be68610e80..fb688e7a7b6 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s +++ b/hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s @@ -38,28 +38,6 @@ .text - .globl SafeFetch32, Fetch32PFI, Fetch32Resume - .align 16 - .type SafeFetch32,@function - // Prototype: int SafeFetch32 (int * Adr, int ErrValue) -SafeFetch32: - movl %esi, %eax -Fetch32PFI: - movl (%rdi), %eax -Fetch32Resume: - ret - - .globl SafeFetchN, FetchNPFI, FetchNResume - .align 16 - .type SafeFetchN,@function - // Prototype: intptr_t SafeFetchN (intptr_t * Adr, intptr_t ErrValue) -SafeFetchN: - movq %rsi, %rax -FetchNPFI: - movq (%rdi), %rax -FetchNResume: - ret - .globl SpinPause .align 16 .type SpinPause,@function diff --git a/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp index 7a9fd1a4413..10240a0b1c2 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp +++ b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp @@ -72,7 +72,7 @@ inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } inline julong OrderAccess::load_acquire(volatile julong* p) { return Atomic::load((volatile jlong*)p); } inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } -inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return jdouble_cast(Atomic::load((volatile jlong*)p)); } inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } @@ -87,7 +87,7 @@ inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } inline void OrderAccess::release_store(volatile julong* p, julong v) { Atomic::store((jlong)v, (volatile jlong*)p); } inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } -inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { release_store((volatile jlong *)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } @@ -129,7 +129,7 @@ inline void OrderAccess::store_fence(jushort* p, jushort v) { store_fence((j inline void OrderAccess::store_fence(juint* p, juint v) { store_fence((jint*)p, (jint)v); } inline void OrderAccess::store_fence(julong* p, julong v) { store_fence((jlong*)p, (jlong)v); } inline void OrderAccess::store_fence(jfloat* p, jfloat v) { *p = v; fence(); } -inline void OrderAccess::store_fence(jdouble* p, jdouble v) { *p = v; fence(); } +inline void OrderAccess::store_fence(jdouble* p, jdouble v) { store_fence((jlong*)p, jlong_cast(v)); } inline void OrderAccess::store_ptr_fence(intptr_t* p, intptr_t v) { #ifdef AMD64 @@ -190,7 +190,7 @@ inline void OrderAccess::release_store_fence(volatile juint* p, juint v) inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store_fence((volatile jlong*)p, (jlong)v); } inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } -inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store_fence((volatile jlong*)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { #ifdef AMD64 diff --git a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp index 4fc3b76d228..8878c0ea353 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp +++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp @@ -209,13 +209,6 @@ enum { trap_page_fault = 0xE }; -extern "C" void Fetch32PFI () ; -extern "C" void Fetch32Resume () ; -#ifdef AMD64 -extern "C" void FetchNPFI () ; -extern "C" void FetchNResume () ; -#endif // AMD64 - extern "C" JNIEXPORT int JVM_handle_linux_signal(int sig, siginfo_t* info, @@ -225,6 +218,10 @@ JVM_handle_linux_signal(int sig, Thread* t = ThreadLocalStorage::get_thread_slow(); + // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away + // (no destructors can be run) + os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + SignalHandlerMark shm(t); // Note: it's not uncommon that JNI code uses signal/sigset to install @@ -278,16 +275,10 @@ JVM_handle_linux_signal(int sig, if (info != NULL && uc != NULL && thread != NULL) { pc = (address) os::Linux::ucontext_get_pc(uc); - if (pc == (address) Fetch32PFI) { - uc->uc_mcontext.gregs[REG_PC] = intptr_t(Fetch32Resume) ; - return 1 ; + if (StubRoutines::is_safefetch_fault(pc)) { + uc->uc_mcontext.gregs[REG_PC] = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + return 1; } -#ifdef AMD64 - if (pc == (address) FetchNPFI) { - uc->uc_mcontext.gregs[REG_PC] = intptr_t (FetchNResume) ; - return 1 ; - } -#endif // AMD64 #ifndef AMD64 // Halt if SI_KERNEL before more crashes get misdiagnosed as Java bugs diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp index 939def32fec..d8c08fd4a63 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp +++ b/hotspot/src/os_cpu/solaris_sparc/vm/os_solaris_sparc.cpp @@ -303,11 +303,6 @@ bool os::is_allocatable(size_t bytes) { #endif } -extern "C" void Fetch32PFI () ; -extern "C" void Fetch32Resume () ; -extern "C" void FetchNPFI () ; -extern "C" void FetchNResume () ; - extern "C" JNIEXPORT int JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { @@ -315,6 +310,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, Thread* t = ThreadLocalStorage::get_thread_slow(); + // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away + // (no destructors can be run) + os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + SignalHandlerMark shm(t); if(sig == SIGPIPE || sig == SIGXFSZ) { @@ -379,17 +378,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, npc = (address) uc->uc_mcontext.gregs[REG_nPC]; // SafeFetch() support - // Implemented with either a fixed set of addresses such - // as Fetch32*, or with Thread._OnTrap. - if (uc->uc_mcontext.gregs[REG_PC] == intptr_t(Fetch32PFI)) { - uc->uc_mcontext.gregs [REG_PC] = intptr_t(Fetch32Resume) ; - uc->uc_mcontext.gregs [REG_nPC] = intptr_t(Fetch32Resume) + 4 ; - return true ; - } - if (uc->uc_mcontext.gregs[REG_PC] == intptr_t(FetchNPFI)) { - uc->uc_mcontext.gregs [REG_PC] = intptr_t(FetchNResume) ; - uc->uc_mcontext.gregs [REG_nPC] = intptr_t(FetchNResume) + 4 ; - return true ; + if (StubRoutines::is_safefetch_fault(pc)) { + uc->uc_mcontext.gregs[REG_PC] = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + uc->uc_mcontext.gregs[REG_nPC] = uc->uc_mcontext.gregs[REG_PC] + 4; + return 1; } // Handle ALL stack overflow variations here diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s index aa526a09d08..39aaa77f664 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s +++ b/hotspot/src/os_cpu/solaris_sparc/vm/solaris_sparc.s @@ -21,47 +21,6 @@ !! questions. !! - !! Prototype: int SafeFetch32 (int * adr, int ErrValue) - !! The "ld" at Fetch32 is potentially faulting instruction. - !! If the instruction traps the trap handler will arrange - !! for control to resume at Fetch32Resume. - !! By convention with the trap handler we ensure there is a non-CTI - !! instruction in the trap shadow. - !! - !! The reader might be tempted to move this service to .il. - !! Don't. Sun's CC back-end reads and optimize code emitted - !! by the .il "call", in some cases optimizing the code, completely eliding it, - !! or by moving the code from the "call site". - - !! ASM better know we may use G6 for our own purposes - .register %g6, #ignore - - .globl SafeFetch32 - .align 32 - .global Fetch32PFI, Fetch32Resume -SafeFetch32: - mov %o0, %g1 - mov %o1, %o0 -Fetch32PFI: - ld [%g1], %o0 !! <-- Potentially faulting instruction -Fetch32Resume: - nop - retl - nop - - .globl SafeFetchN - .align 32 - .globl FetchNPFI, FetchNResume -SafeFetchN: - mov %o0, %g1 - mov %o1, %o0 -FetchNPFI: - ldn [%g1], %o0 -FetchNResume: - nop - retl - nop - !! Possibilities: !! -- membar !! -- CAS (SP + BIAS, G0, G0) diff --git a/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp index c02d98ed991..e7238c27549 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp @@ -88,7 +88,7 @@ inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } inline julong OrderAccess::load_acquire(volatile julong* p) { return Atomic::load((volatile jlong*)p); } inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } -inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return jdouble_cast(Atomic::load((volatile jlong*)p)); } inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } @@ -103,7 +103,7 @@ inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } inline void OrderAccess::release_store(volatile julong* p, julong v) { Atomic::store((jlong)v, (volatile jlong*)p); } inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } -inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { release_store((volatile jlong*)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } @@ -129,9 +129,9 @@ inline void OrderAccess::release_store_fence(volatile jlong* p, jlong v) inline void OrderAccess::release_store_fence(volatile jubyte* p, jubyte v) { *p = v; fence(); } inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) { *p = v; fence(); } inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { *p = v; fence(); } -inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store(p, v); fence(); } +inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store((jlong *)p, (jlong)v); fence(); } inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } -inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store_fence((volatile jlong*)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { *p = v; fence(); } inline void OrderAccess::release_store_ptr_fence(volatile void* p, void* v) { *(void* volatile *)p = v; fence(); } diff --git a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp index 4ed094db734..eb8cbe81918 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp @@ -352,13 +352,6 @@ bool os::is_allocatable(size_t bytes) { } -extern "C" void Fetch32PFI () ; -extern "C" void Fetch32Resume () ; -#ifdef AMD64 -extern "C" void FetchNPFI () ; -extern "C" void FetchNResume () ; -#endif // AMD64 - extern "C" JNIEXPORT int JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { @@ -374,6 +367,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, Thread* t = ThreadLocalStorage::get_thread_slow(); // slow & steady + // Must do this before SignalHandlerMark, if crash protection installed we will longjmp away + // (no destructors can be run) + os::WatcherThreadCrashProtection::check_crash_protection(sig, t); + SignalHandlerMark shm(t); if(sig == SIGPIPE || sig == SIGXFSZ) { @@ -436,17 +433,10 @@ JVM_handle_solaris_signal(int sig, siginfo_t* info, void* ucVoid, // factor me: getPCfromContext pc = (address) uc->uc_mcontext.gregs[REG_PC]; - // SafeFetch32() support - if (pc == (address) Fetch32PFI) { - uc->uc_mcontext.gregs[REG_PC] = intptr_t(Fetch32Resume) ; - return true ; + if (StubRoutines::is_safefetch_fault(pc)) { + uc->uc_mcontext.gregs[REG_PC] = intptr_t(StubRoutines::continuation_for_safefetch_fault(pc)); + return true; } -#ifdef AMD64 - if (pc == (address) FetchNPFI) { - uc->uc_mcontext.gregs [REG_PC] = intptr_t(FetchNResume) ; - return true ; - } -#endif // AMD64 // Handle ALL stack overflow variations here if (sig == SIGSEGV && info->si_code == SEGV_ACCERR) { diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s index 1fac3b25f11..19e790b6013 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_32.s @@ -54,20 +54,6 @@ fixcw: popl %eax ret - .align 16 - .globl SafeFetch32 - .globl SafeFetchN - .globl Fetch32PFI, Fetch32Resume -SafeFetch32: -SafeFetchN: - movl 0x8(%esp), %eax - movl 0x4(%esp), %ecx -Fetch32PFI: - movl (%ecx), %eax -Fetch32Resume: - ret - - .align 16 .globl SpinPause SpinPause: diff --git a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s index 95050af24f0..487b569e58c 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s +++ b/hotspot/src/os_cpu/solaris_x86/vm/solaris_x86_64.s @@ -21,54 +21,34 @@ / questions. / - .globl fs_load - .globl fs_thread + .globl fs_load + .globl fs_thread // NOTE WELL! The _Copy functions are called directly - // from server-compiler-generated code via CallLeafNoFP, - // which means that they *must* either not use floating - // point or use it in the same manner as does the server - // compiler. + // from server-compiler-generated code via CallLeafNoFP, + // which means that they *must* either not use floating + // point or use it in the same manner as does the server + // compiler. .globl _Copy_arrayof_conjoint_bytes .globl _Copy_conjoint_jshorts_atomic - .globl _Copy_arrayof_conjoint_jshorts + .globl _Copy_arrayof_conjoint_jshorts .globl _Copy_conjoint_jints_atomic .globl _Copy_arrayof_conjoint_jints - .globl _Copy_conjoint_jlongs_atomic + .globl _Copy_conjoint_jlongs_atomic .globl _Copy_arrayof_conjoint_jlongs - .section .text,"ax" + .section .text,"ax" / Fast thread accessors, used by threadLS_solaris_amd64.cpp - .align 16 + .align 16 fs_load: - movq %fs:(%rdi),%rax - ret - - .align 16 -fs_thread: - movq %fs:0x0,%rax - ret - - .globl SafeFetch32, Fetch32PFI, Fetch32Resume - .align 16 - // Prototype: int SafeFetch32 (int * Adr, int ErrValue) -SafeFetch32: - movl %esi, %eax -Fetch32PFI: - movl (%rdi), %eax -Fetch32Resume: + movq %fs:(%rdi),%rax ret - .globl SafeFetchN, FetchNPFI, FetchNResume - .align 16 - // Prototype: intptr_t SafeFetchN (intptr_t * Adr, intptr_t ErrValue) -SafeFetchN: - movq %rsi, %rax -FetchNPFI: - movq (%rdi), %rax -FetchNResume: + .align 16 +fs_thread: + movq %fs:0x0,%rax ret .globl SpinPause @@ -78,7 +58,7 @@ SpinPause: nop movq $1, %rax ret - + / Support for void Copy::arrayof_conjoint_bytes(void* from, / void* to, @@ -340,7 +320,7 @@ aci_CopyLeft: addq $4,%rdx jg 1b ret - + / Support for void Copy::arrayof_conjoint_jlongs(jlong* from, / jlong* to, / size_t count) diff --git a/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp index 639b8ad9d9d..ea0ed143e98 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp +++ b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp @@ -71,7 +71,7 @@ inline jushort OrderAccess::load_acquire(volatile jushort* p) { return *p; } inline juint OrderAccess::load_acquire(volatile juint* p) { return *p; } inline julong OrderAccess::load_acquire(volatile julong* p) { return Atomic::load((volatile jlong*)p); } inline jfloat OrderAccess::load_acquire(volatile jfloat* p) { return *p; } -inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return *p; } +inline jdouble OrderAccess::load_acquire(volatile jdouble* p) { return jdouble_cast(Atomic::load((volatile jlong*)p)); } inline intptr_t OrderAccess::load_ptr_acquire(volatile intptr_t* p) { return *p; } inline void* OrderAccess::load_ptr_acquire(volatile void* p) { return *(void* volatile *)p; } @@ -86,7 +86,7 @@ inline void OrderAccess::release_store(volatile jushort* p, jushort v) { *p inline void OrderAccess::release_store(volatile juint* p, juint v) { *p = v; } inline void OrderAccess::release_store(volatile julong* p, julong v) { Atomic::store((jlong)v, (volatile jlong*)p); } inline void OrderAccess::release_store(volatile jfloat* p, jfloat v) { *p = v; } -inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { *p = v; } +inline void OrderAccess::release_store(volatile jdouble* p, jdouble v) { release_store((volatile jlong*)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr(volatile intptr_t* p, intptr_t v) { *p = v; } inline void OrderAccess::release_store_ptr(volatile void* p, void* v) { *(void* volatile *)p = v; } @@ -195,7 +195,7 @@ inline void OrderAccess::release_store_fence(volatile jushort* p, jushort v) inline void OrderAccess::release_store_fence(volatile juint* p, juint v) { release_store_fence((volatile jint*)p, (jint)v); } inline void OrderAccess::release_store_fence(volatile julong* p, julong v) { release_store_fence((volatile jlong*)p, (jlong)v); } inline void OrderAccess::release_store_fence(volatile jfloat* p, jfloat v) { *p = v; fence(); } -inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { *p = v; fence(); } +inline void OrderAccess::release_store_fence(volatile jdouble* p, jdouble v) { release_store_fence((volatile jlong*)p, jlong_cast(v)); } inline void OrderAccess::release_store_ptr_fence(volatile intptr_t* p, intptr_t v) { #ifdef AMD64 diff --git a/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp b/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp index 1ef29f99a55..a0f2a7680be 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp +++ b/hotspot/src/os_cpu/windows_x86/vm/os_windows_x86.cpp @@ -518,24 +518,6 @@ void os::print_register_info(outputStream *st, void *context) { st->cr(); } -extern "C" int SafeFetch32 (int * adr, int Err) { - int rv = Err ; - _try { - rv = *((volatile int *) adr) ; - } __except(EXCEPTION_EXECUTE_HANDLER) { - } - return rv ; -} - -extern "C" intptr_t SafeFetchN (intptr_t * adr, intptr_t Err) { - intptr_t rv = Err ; - _try { - rv = *((volatile intptr_t *) adr) ; - } __except(EXCEPTION_EXECUTE_HANDLER) { - } - return rv ; -} - extern "C" int SpinPause () { #ifdef AMD64 return 0 ; diff --git a/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp index 00b9b6c305d..f4ea83debf4 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp +++ b/hotspot/src/os_cpu/windows_x86/vm/unwind_windows_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2013, 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 @@ -29,10 +29,15 @@ #ifdef AMD64 typedef unsigned char UBYTE; +#if _MSC_VER < 1700 + +/* Not needed for VS2012 compiler, comes from winnt.h. */ #define UNW_FLAG_EHANDLER 0x01 #define UNW_FLAG_UHANDLER 0x02 #define UNW_FLAG_CHAININFO 0x04 +#endif + // This structure is used to define an UNWIND_INFO that // only has an ExceptionHandler. There are no UnwindCodes // declared. @@ -59,6 +64,9 @@ typedef struct _RUNTIME_FUNCTION { } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; */ +#if _MSC_VER < 1700 + +/* Not needed for VS2012 compiler, comes from winnt.h. */ typedef struct _DISPATCHER_CONTEXT { ULONG64 ControlPc; ULONG64 ImageBase; @@ -71,6 +79,8 @@ typedef struct _DISPATCHER_CONTEXT { PVOID HandlerData; } DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT; +#endif + #if _MSC_VER < 1500 /* Not needed for VS2008 compiler, comes from winnt.h. */ diff --git a/hotspot/src/share/vm/adlc/forms.hpp b/hotspot/src/share/vm/adlc/forms.hpp index a682e65a36f..63e367dd730 100644 --- a/hotspot/src/share/vm/adlc/forms.hpp +++ b/hotspot/src/share/vm/adlc/forms.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -146,7 +146,7 @@ public: // Public Methods Form(int formType=0, int line=0) : _next(NULL), _linenum(line), _ftype(formType) { }; - ~Form() {}; + virtual ~Form() {}; virtual bool ideal_only() const { assert(0,"Check of ideal status on non-instruction/operand form.\n"); diff --git a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp index 32dc35d8059..e2fca4845b1 100644 --- a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp +++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp @@ -138,6 +138,16 @@ bool BCEscapeAnalyzer::is_arg_stack(ArgumentMap vars){ return false; } +// return true if all argument elements of vars are returned +bool BCEscapeAnalyzer::returns_all(ArgumentMap vars) { + for (int i = 0; i < _arg_size; i++) { + if (vars.contains(i) && !_arg_returned.test(i)) { + return false; + } + } + return true; +} + void BCEscapeAnalyzer::clear_bits(ArgumentMap vars, VectorSet &bm) { for (int i = 0; i < _arg_size; i++) { if (vars.contains(i)) { @@ -166,6 +176,11 @@ void BCEscapeAnalyzer::set_global_escape(ArgumentMap vars, bool merge) { if (vars.contains_unknown() || vars.contains_vars()) { _return_allocated = false; } + if (_return_local && vars.contains_vars() && !returns_all(vars)) { + // Return result should be invalidated if args in new + // state are not recorded in return state. + _return_local = false; + } } } diff --git a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp index 99a8adccbf7..3c701b6a4e8 100644 --- a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp +++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.hpp @@ -80,6 +80,7 @@ class BCEscapeAnalyzer : public ResourceObj { void set_returned(ArgumentMap vars); bool is_argument(ArgumentMap vars); bool is_arg_stack(ArgumentMap vars); + bool returns_all(ArgumentMap vars); void clear_bits(ArgumentMap vars, VectorSet &bs); void set_method_escape(ArgumentMap vars); void set_global_escape(ArgumentMap vars, bool merge = false); diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index 65cd2333a9d..3ffacd39aff 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -3647,8 +3647,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, // If RedefineClasses() was used before the retransformable // agent attached, then the cached class bytes may not be the // original class bytes. - unsigned char *cached_class_file_bytes = NULL; - jint cached_class_file_length; + JvmtiCachedClassFileData *cached_class_file = NULL; Handle class_loader(THREAD, loader_data->class_loader()); bool has_default_methods = false; ResourceMark rm(THREAD); @@ -3680,10 +3679,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, if (h_class_being_redefined != NULL) { instanceKlassHandle ikh_class_being_redefined = instanceKlassHandle(THREAD, (*h_class_being_redefined)()); - cached_class_file_bytes = - ikh_class_being_redefined->get_cached_class_file_bytes(); - cached_class_file_length = - ikh_class_being_redefined->get_cached_class_file_len(); + cached_class_file = ikh_class_being_redefined->get_cached_class_file(); } } @@ -3691,9 +3687,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, unsigned char* end_ptr = cfs->buffer() + cfs->length(); JvmtiExport::post_class_file_load_hook(name, class_loader(), protection_domain, - &ptr, &end_ptr, - &cached_class_file_bytes, - &cached_class_file_length); + &ptr, &end_ptr, &cached_class_file); if (ptr != cfs->buffer()) { // JVMTI agent has modified class file data. @@ -4011,10 +4005,9 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, } } - if (cached_class_file_bytes != NULL) { + if (cached_class_file != NULL) { // JVMTI: we have an InstanceKlass now, tell it about the cached bytes - this_klass->set_cached_class_file(cached_class_file_bytes, - cached_class_file_length); + this_klass->set_cached_class_file(cached_class_file); } // Fill in field values obtained by parse_classfile_attributes diff --git a/hotspot/src/share/vm/classfile/defaultMethods.cpp b/hotspot/src/share/vm/classfile/defaultMethods.cpp index d6529ca4439..3ff2996b3ed 100644 --- a/hotspot/src/share/vm/classfile/defaultMethods.cpp +++ b/hotspot/src/share/vm/classfile/defaultMethods.cpp @@ -318,17 +318,17 @@ class KeepAliveVisitor : public HierarchyVisitor { } }; + // A method family contains a set of all methods that implement a single -// language-level method. Because of erasure, these methods may have different -// signatures. As members of the set are collected while walking over the +// erased method. As members of the set are collected while walking over the // hierarchy, they are tagged with a qualification state. The qualification // state for an erased method is set to disqualified if there exists a path // from the root of hierarchy to the method that contains an interleaving -// language-equivalent method defined in an interface. +// erased method defined in an interface. + class MethodFamily : public ResourceObj { private: - generic::MethodDescriptor* _descriptor; // language-level description GrowableArray > _members; ResourceHashtable _member_index; @@ -358,15 +358,8 @@ class MethodFamily : public ResourceObj { public: - MethodFamily(generic::MethodDescriptor* canonical_desc) - : _descriptor(canonical_desc), _selected_target(NULL), - _exception_message(NULL) {} - - generic::MethodDescriptor* descriptor() const { return _descriptor; } - - bool descriptor_matches(generic::MethodDescriptor* md, generic::Context* ctx) { - return descriptor()->covariant_match(md, ctx); - } + MethodFamily() + : _selected_target(NULL), _exception_message(NULL) {} void set_target_if_empty(Method* m) { if (_selected_target == NULL && !m->is_overpass()) { @@ -441,16 +434,10 @@ class MethodFamily : public ResourceObj { } #ifndef PRODUCT - void print_on(outputStream* str) const { - print_on(str, 0); - } - - void print_on(outputStream* str, int indent) const { + void print_sig_on(outputStream* str, Symbol* signature, int indent) const { streamIndentor si(str, indent * 2); - generic::Context ctx(NULL); // empty, as _descriptor already canonicalized - TempNewSymbol family = descriptor()->reify_signature(&ctx, Thread::current()); - str->indent().print_cr("Logical Method %s:", family->as_C_string()); + str->indent().print_cr("Logical Method %s:", signature->as_C_string()); streamIndentor si2(str); for (int i = 0; i < _members.length(); ++i) { @@ -516,38 +503,94 @@ Symbol* MethodFamily::generate_conflicts_message(GrowableArray* methods return SymbolTable::new_symbol(ss.base(), (int)ss.size(), CHECK_NULL); } +// A generic method family contains a set of all methods that implement a single +// language-level method. Because of erasure, these methods may have different +// signatures. As members of the set are collected while walking over the +// hierarchy, they are tagged with a qualification state. The qualification +// state for an erased method is set to disqualified if there exists a path +// from the root of hierarchy to the method that contains an interleaving +// language-equivalent method defined in an interface. +class GenericMethodFamily : public MethodFamily { + private: + + generic::MethodDescriptor* _descriptor; // language-level description + + public: + + GenericMethodFamily(generic::MethodDescriptor* canonical_desc) + : _descriptor(canonical_desc) {} + + generic::MethodDescriptor* descriptor() const { return _descriptor; } + + bool descriptor_matches(generic::MethodDescriptor* md, generic::Context* ctx) { + return descriptor()->covariant_match(md, ctx); + } + +#ifndef PRODUCT + Symbol* get_generic_sig() const { + + generic::Context ctx(NULL); // empty, as _descriptor already canonicalized + TempNewSymbol sig = descriptor()->reify_signature(&ctx, Thread::current()); + return sig; + } +#endif // ndef PRODUCT +}; + class StateRestorer; -// StatefulMethodFamily is a wrapper around MethodFamily that maintains the +// StatefulMethodFamily is a wrapper around a MethodFamily that maintains the // qualification state during hierarchy visitation, and applies that state -// when adding members to the MethodFamily. +// when adding members to the MethodFamily class StatefulMethodFamily : public ResourceObj { friend class StateRestorer; private: - MethodFamily* _method; QualifiedState _qualification_state; void set_qualification_state(QualifiedState state) { _qualification_state = state; } + protected: + MethodFamily* _method_family; + public: - StatefulMethodFamily(generic::MethodDescriptor* md, generic::Context* ctx) { - _method = new MethodFamily(md->canonicalize(ctx)); - _qualification_state = QUALIFIED; + StatefulMethodFamily() { + _method_family = new MethodFamily(); + _qualification_state = QUALIFIED; } - void set_target_if_empty(Method* m) { _method->set_target_if_empty(m); } - - MethodFamily* get_method_family() { return _method; } - - bool descriptor_matches(generic::MethodDescriptor* md, generic::Context* ctx) { - return _method->descriptor_matches(md, ctx); + StatefulMethodFamily(MethodFamily* mf) { + _method_family = mf; + _qualification_state = QUALIFIED; } + void set_target_if_empty(Method* m) { _method_family->set_target_if_empty(m); } + + MethodFamily* get_method_family() { return _method_family; } + StateRestorer* record_method_and_dq_further(Method* mo); }; + +// StatefulGenericMethodFamily is a wrapper around GenericMethodFamily that maintains the +// qualification state during hierarchy visitation, and applies that state +// when adding members to the GenericMethodFamily. +class StatefulGenericMethodFamily : public StatefulMethodFamily { + + public: + StatefulGenericMethodFamily(generic::MethodDescriptor* md, generic::Context* ctx) + : StatefulMethodFamily(new GenericMethodFamily(md->canonicalize(ctx))) { + + } + GenericMethodFamily* get_method_family() { + return (GenericMethodFamily*)_method_family; + } + + bool descriptor_matches(generic::MethodDescriptor* md, generic::Context* ctx) { + return get_method_family()->descriptor_matches(md, ctx); + } +}; + class StateRestorer : public PseudoScopeMark { private: StatefulMethodFamily* _method; @@ -563,9 +606,9 @@ class StateRestorer : public PseudoScopeMark { StateRestorer* StatefulMethodFamily::record_method_and_dq_further(Method* mo) { StateRestorer* mark = new StateRestorer(this, _qualification_state); if (_qualification_state == QUALIFIED) { - _method->record_qualified_method(mo); + _method_family->record_qualified_method(mo); } else { - _method->record_disqualified_method(mo); + _method_family->record_disqualified_method(mo); } // Everything found "above"??? this method in the hierarchy walk is set to // disqualified @@ -573,15 +616,15 @@ StateRestorer* StatefulMethodFamily::record_method_and_dq_further(Method* mo) { return mark; } -class StatefulMethodFamilies : public ResourceObj { +class StatefulGenericMethodFamilies : public ResourceObj { private: - GrowableArray _methods; + GrowableArray _methods; public: - StatefulMethodFamily* find_matching( + StatefulGenericMethodFamily* find_matching( generic::MethodDescriptor* md, generic::Context* ctx) { for (int i = 0; i < _methods.length(); ++i) { - StatefulMethodFamily* existing = _methods.at(i); + StatefulGenericMethodFamily* existing = _methods.at(i); if (existing->descriptor_matches(md, ctx)) { return existing; } @@ -589,17 +632,17 @@ class StatefulMethodFamilies : public ResourceObj { return NULL; } - StatefulMethodFamily* find_matching_or_create( + StatefulGenericMethodFamily* find_matching_or_create( generic::MethodDescriptor* md, generic::Context* ctx) { - StatefulMethodFamily* method = find_matching(md, ctx); + StatefulGenericMethodFamily* method = find_matching(md, ctx); if (method == NULL) { - method = new StatefulMethodFamily(md, ctx); + method = new StatefulGenericMethodFamily(md, ctx); _methods.append(method); } return method; } - void extract_families_into(GrowableArray* array) { + void extract_families_into(GrowableArray* array) { for (int i = 0; i < _methods.length(); ++i) { array->append(_methods.at(i)->get_method_family()); } @@ -683,26 +726,79 @@ static GrowableArray* find_empty_vtable_slots( return slots; } +// Iterates over the superinterface type hierarchy looking for all methods +// with a specific erased signature. +class FindMethodsByErasedSig : public HierarchyVisitor { + private: + // Context data + Symbol* _method_name; + Symbol* _method_signature; + StatefulMethodFamily* _family; + + public: + FindMethodsByErasedSig(Symbol* name, Symbol* signature) : + _method_name(name), _method_signature(signature), + _family(NULL) {} + + void get_discovered_family(MethodFamily** family) { + if (_family != NULL) { + *family = _family->get_method_family(); + } else { + *family = NULL; + } + } + + void* new_node_data(InstanceKlass* cls) { return new PseudoScope(); } + void free_node_data(void* node_data) { + PseudoScope::cast(node_data)->destroy(); + } + + // Find all methods on this hierarchy that match this + // method's erased (name, signature) + bool visit() { + PseudoScope* scope = PseudoScope::cast(current_data()); + InstanceKlass* iklass = current_class(); + + Method* m = iklass->find_method(_method_name, _method_signature); + if (m != NULL) { + if (_family == NULL) { + _family = new StatefulMethodFamily(); + } + + if (iklass->is_interface()) { + StateRestorer* restorer = _family->record_method_and_dq_further(m); + scope->add_mark(restorer); + } else { + // This is the rule that methods in classes "win" (bad word) over + // methods in interfaces. This works because of single inheritance + _family->set_target_if_empty(m); + } + } + return true; + } + +}; + // Iterates over the type hierarchy looking for all methods with a specific // method name. The result of this is a set of method families each of // which is populated with a set of methods that implement the same // language-level signature. -class FindMethodsByName : public HierarchyVisitor { +class FindMethodsByGenericSig : public HierarchyVisitor { private: // Context data Thread* THREAD; generic::DescriptorCache* _cache; Symbol* _method_name; generic::Context* _ctx; - StatefulMethodFamilies _families; + StatefulGenericMethodFamilies _families; public: - FindMethodsByName(generic::DescriptorCache* cache, Symbol* name, + FindMethodsByGenericSig(generic::DescriptorCache* cache, Symbol* name, generic::Context* ctx, Thread* thread) : _cache(cache), _method_name(name), _ctx(ctx), THREAD(thread) {} - void get_discovered_families(GrowableArray* methods) { + void get_discovered_families(GrowableArray* methods) { _families.extract_families_into(methods); } @@ -733,7 +829,7 @@ class FindMethodsByName : public HierarchyVisitor { // Find all methods on this hierarchy that match this method // (name, signature). This class collects other families of this // method name. - StatefulMethodFamily* family = + StatefulGenericMethodFamily* family = _families.find_matching_or_create(md, _ctx); if (klass->is_interface()) { @@ -752,8 +848,8 @@ class FindMethodsByName : public HierarchyVisitor { }; #ifndef PRODUCT -static void print_families( - GrowableArray* methods, Symbol* match) { +static void print_generic_families( + GrowableArray* methods, Symbol* match) { streamIndentor si(tty, 4); if (methods->length() == 0) { tty->indent(); @@ -761,22 +857,87 @@ static void print_families( } for (int i = 0; i < methods->length(); ++i) { tty->indent(); - MethodFamily* lm = methods->at(i); + GenericMethodFamily* lm = methods->at(i); if (lm->contains_signature(match)) { tty->print_cr(""); } else { tty->print_cr(""); } - lm->print_on(tty, 1); + lm->print_sig_on(tty, lm->get_generic_sig(), 1); } } #endif // ndef PRODUCT -static void merge_in_new_methods(InstanceKlass* klass, - GrowableArray* new_methods, TRAPS); static void create_overpasses( GrowableArray* slots, InstanceKlass* klass, TRAPS); +static void generate_generic_defaults( + InstanceKlass* klass, GrowableArray* empty_slots, + EmptyVtableSlot* slot, int current_slot_index, TRAPS) { + + if (slot->is_bound()) { +#ifndef PRODUCT + if (TraceDefaultMethods) { + streamIndentor si(tty, 4); + tty->indent().print_cr("Already bound to logical method:"); + GenericMethodFamily* lm = (GenericMethodFamily*)(slot->get_binding()); + lm->print_sig_on(tty, lm->get_generic_sig(), 1); + } +#endif // ndef PRODUCT + return; // covered by previous processing + } + + generic::DescriptorCache cache; + + generic::Context ctx(&cache); + FindMethodsByGenericSig visitor(&cache, slot->name(), &ctx, CHECK); + visitor.run(klass); + + GrowableArray discovered_families; + visitor.get_discovered_families(&discovered_families); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + print_generic_families(&discovered_families, slot->signature()); + } +#endif // ndef PRODUCT + + // Find and populate any other slots that match the discovered families + for (int j = current_slot_index; j < empty_slots->length(); ++j) { + EmptyVtableSlot* open_slot = empty_slots->at(j); + + if (slot->name() == open_slot->name()) { + for (int k = 0; k < discovered_families.length(); ++k) { + GenericMethodFamily* lm = discovered_families.at(k); + + if (lm->contains_signature(open_slot->signature())) { + lm->determine_target(klass, CHECK); + open_slot->bind_family(lm); + } + } + } + } +} + +static void generate_erased_defaults( + InstanceKlass* klass, GrowableArray* empty_slots, + EmptyVtableSlot* slot, TRAPS) { + + // sets up a set of methods with the same exact erased signature + FindMethodsByErasedSig visitor(slot->name(), slot->signature()); + visitor.run(klass); + + MethodFamily* family; + visitor.get_discovered_family(&family); + if (family != NULL) { + family->determine_target(klass, CHECK); + slot->bind_family(family); + } +} + +static void merge_in_new_methods(InstanceKlass* klass, + GrowableArray* new_methods, TRAPS); + // This is the guts of the default methods implementation. This is called just // after the classfile has been parsed if some ancestor has default methods. // @@ -807,8 +968,6 @@ void DefaultMethods::generate_default_methods( // whatever scope it's in. ResourceMark rm(THREAD); - generic::DescriptorCache cache; - // Keep entire hierarchy alive for the duration of the computation KeepAliveRegistrar keepAlive(THREAD); KeepAliveVisitor loadKeepAlive(&keepAlive); @@ -837,47 +996,13 @@ void DefaultMethods::generate_default_methods( tty->print_cr(""); } #endif // ndef PRODUCT - if (slot->is_bound()) { -#ifndef PRODUCT - if (TraceDefaultMethods) { - streamIndentor si(tty, 4); - tty->indent().print_cr("Already bound to logical method:"); - slot->get_binding()->print_on(tty, 1); - } -#endif // ndef PRODUCT - continue; // covered by previous processing + + if (ParseGenericDefaults) { + generate_generic_defaults(klass, empty_slots, slot, i, CHECK); + } else { + generate_erased_defaults(klass, empty_slots, slot, CHECK); } - - generic::Context ctx(&cache); - FindMethodsByName visitor(&cache, slot->name(), &ctx, CHECK); - visitor.run(klass); - - GrowableArray discovered_families; - visitor.get_discovered_families(&discovered_families); - -#ifndef PRODUCT - if (TraceDefaultMethods) { - print_families(&discovered_families, slot->signature()); - } -#endif // ndef PRODUCT - - // Find and populate any other slots that match the discovered families - for (int j = i; j < empty_slots->length(); ++j) { - EmptyVtableSlot* open_slot = empty_slots->at(j); - - if (slot->name() == open_slot->name()) { - for (int k = 0; k < discovered_families.length(); ++k) { - MethodFamily* lm = discovered_families.at(k); - - if (lm->contains_signature(open_slot->signature())) { - lm->determine_target(klass, CHECK); - open_slot->bind_family(lm); - } - } - } - } - } - + } #ifndef PRODUCT if (TraceDefaultMethods) { tty->print_cr("Creating overpasses..."); @@ -893,7 +1018,6 @@ void DefaultMethods::generate_default_methods( #endif // ndef PRODUCT } - /** * Generic analysis was used upon interface '_target' and found a unique * default method candidate with generic signature '_method_desc'. This @@ -912,17 +1036,85 @@ void DefaultMethods::generate_default_methods( * the selected method along that path. */ class ShadowChecker : public HierarchyVisitor { - private: - generic::DescriptorCache* _cache; + protected: Thread* THREAD; InstanceKlass* _target; Symbol* _method_name; InstanceKlass* _method_holder; - generic::MethodDescriptor* _method_desc; bool _found_shadow; + + public: + + ShadowChecker(Thread* thread, Symbol* name, InstanceKlass* holder, + InstanceKlass* target) + : THREAD(thread), _method_name(name), _method_holder(holder), + _target(target), _found_shadow(false) {} + + void* new_node_data(InstanceKlass* cls) { return NULL; } + void free_node_data(void* data) { return; } + + bool visit() { + InstanceKlass* ik = current_class(); + if (ik == _target && current_depth() == 1) { + return false; // This was the specified super -- no need to search it + } + if (ik == _method_holder || ik == _target) { + // We found a path that should be examined to see if it shadows _method + if (path_has_shadow()) { + _found_shadow = true; + cancel_iteration(); + } + return false; // no need to continue up hierarchy + } + return true; + } + + virtual bool path_has_shadow() = 0; + bool found_shadow() { return _found_shadow; } +}; + +// Used for Invokespecial. +// Invokespecial is allowed to invoke a concrete interface method +// and can be used to disambuiguate among qualified candidates, +// which are methods in immediate superinterfaces, +// but may not be used to invoke a candidate that would be shadowed +// from the perspective of the caller. +// Invokespecial is also used in the overpass generation today +// We re-run the shadowchecker because we can't distinguish this case, +// but it should return the same answer, since the overpass target +// is now the invokespecial caller. +class ErasedShadowChecker : public ShadowChecker { + private: + bool path_has_shadow() { + + for (int i = current_depth() - 1; i > 0; --i) { + InstanceKlass* ik = class_at_depth(i); + + if (ik->is_interface()) { + int end; + int start = ik->find_method_by_name(_method_name, &end); + if (start != -1) { + return true; + } + } + } + return false; + } + public: + + ErasedShadowChecker(Thread* thread, Symbol* name, InstanceKlass* holder, + InstanceKlass* target) + : ShadowChecker(thread, name, holder, target) {} +}; + +class GenericShadowChecker : public ShadowChecker { + private: + generic::DescriptorCache* _cache; + generic::MethodDescriptor* _method_desc; + bool path_has_shadow() { generic::Context ctx(_cache); @@ -950,104 +1142,42 @@ class ShadowChecker : public HierarchyVisitor { public: - ShadowChecker(generic::DescriptorCache* cache, Thread* thread, + GenericShadowChecker(generic::DescriptorCache* cache, Thread* thread, Symbol* name, InstanceKlass* holder, generic::MethodDescriptor* desc, InstanceKlass* target) - : _cache(cache), THREAD(thread), _method_name(name), _method_holder(holder), - _method_desc(desc), _target(target), _found_shadow(false) {} - - void* new_node_data(InstanceKlass* cls) { return NULL; } - void free_node_data(void* data) { return; } - - bool visit() { - InstanceKlass* ik = current_class(); - if (ik == _target && current_depth() == 1) { - return false; // This was the specified super -- no need to search it - } - if (ik == _method_holder || ik == _target) { - // We found a path that should be examined to see if it shadows _method - if (path_has_shadow()) { - _found_shadow = true; - cancel_iteration(); - } - return false; // no need to continue up hierarchy - } - return true; - } - - bool found_shadow() { return _found_shadow; } + : ShadowChecker(thread, name, holder, target) { + _cache = cache; + _method_desc = desc; + } }; -// This is called during linktime when we find an invokespecial call that -// refers to a direct superinterface. It indicates that we should find the -// default method in the hierarchy of that superinterface, and if that method -// would have been a candidate from the point of view of 'this' class, then we -// return that method. -Method* DefaultMethods::find_super_default( - Klass* cls, Klass* super, Symbol* method_name, Symbol* sig, TRAPS) { - ResourceMark rm(THREAD); - assert(cls != NULL && super != NULL, "Need real classes"); +// Find the unique qualified candidate from the perspective of the super_class +// which is the resolved_klass, which must be an immediate superinterface +// of klass +Method* find_erased_super_default(InstanceKlass* current_class, InstanceKlass* super_class, Symbol* method_name, Symbol* sig, TRAPS) { - InstanceKlass* current_class = InstanceKlass::cast(cls); - InstanceKlass* direction = InstanceKlass::cast(super); + FindMethodsByErasedSig visitor(method_name, sig); + visitor.run(super_class); // find candidates from resolved_klass - // Keep entire hierarchy alive for the duration of the computation - KeepAliveRegistrar keepAlive(THREAD); - KeepAliveVisitor loadKeepAlive(&keepAlive); - loadKeepAlive.run(current_class); + MethodFamily* family; + visitor.get_discovered_family(&family); -#ifndef PRODUCT - if (TraceDefaultMethods) { - tty->print_cr("Finding super default method %s.%s%s from %s", - direction->name()->as_C_string(), - method_name->as_C_string(), sig->as_C_string(), - current_class->name()->as_C_string()); - } -#endif // ndef PRODUCT - - if (!direction->is_interface()) { - // We should not be here - return NULL; + if (family != NULL) { + family->determine_target(current_class, CHECK_NULL); // get target from current_class } - generic::DescriptorCache cache; - generic::Context ctx(&cache); - - // Prime the initial generic context for current -> direction - ctx.apply_type_arguments(current_class, direction, CHECK_NULL); - - FindMethodsByName visitor(&cache, method_name, &ctx, CHECK_NULL); - visitor.run(direction); - - GrowableArray families; - visitor.get_discovered_families(&families); - -#ifndef PRODUCT - if (TraceDefaultMethods) { - print_families(&families, sig); - } -#endif // ndef PRODUCT - - MethodFamily* selected_family = NULL; - - for (int i = 0; i < families.length(); ++i) { - MethodFamily* lm = families.at(i); - if (lm->contains_signature(sig)) { - lm->determine_target(current_class, CHECK_NULL); - selected_family = lm; - } - } - - if (selected_family->has_target()) { - Method* target = selected_family->get_selected_target(); + if (family->has_target()) { + Method* target = family->get_selected_target(); InstanceKlass* holder = InstanceKlass::cast(target->method_holder()); // Verify that the identified method is valid from the context of - // the current class - ShadowChecker checker(&cache, THREAD, target->name(), - holder, selected_family->descriptor(), direction); + // the current class, which is the caller class for invokespecial + // link resolution, i.e. ensure there it is not shadowed. + // You can use invokespecial to disambiguate interface methods, but + // you can not use it to skip over an interface method that would shadow it. + ErasedShadowChecker checker(THREAD, target->name(), holder, super_class); checker.run(current_class); if (checker.found_shadow()) { @@ -1061,13 +1191,71 @@ Method* DefaultMethods::find_super_default( } else { #ifndef PRODUCT if (TraceDefaultMethods) { - tty->print(" Returning "); - print_method(tty, target, true); - tty->print_cr(""); + family->print_sig_on(tty, target->signature(), 1); } #endif // ndef PRODUCT return target; } + } else { + assert(family->throws_exception(), "must have target or throw"); + THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(), + family->get_exception_message()->as_C_string(), NULL); + } +} + +// super_class is assumed to be the direct super of current_class +Method* find_generic_super_default( InstanceKlass* current_class, + InstanceKlass* super_class, + Symbol* method_name, Symbol* sig, TRAPS) { + generic::DescriptorCache cache; + generic::Context ctx(&cache); + + // Prime the initial generic context for current -> super_class + ctx.apply_type_arguments(current_class, super_class, CHECK_NULL); + + FindMethodsByGenericSig visitor(&cache, method_name, &ctx, CHECK_NULL); + visitor.run(super_class); + + GrowableArray families; + visitor.get_discovered_families(&families); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + print_generic_families(&families, sig); + } +#endif // ndef PRODUCT + + GenericMethodFamily* selected_family = NULL; + + for (int i = 0; i < families.length(); ++i) { + GenericMethodFamily* lm = families.at(i); + if (lm->contains_signature(sig)) { + lm->determine_target(current_class, CHECK_NULL); + selected_family = lm; + } + } + + if (selected_family->has_target()) { + Method* target = selected_family->get_selected_target(); + InstanceKlass* holder = InstanceKlass::cast(target->method_holder()); + + // Verify that the identified method is valid from the context of + // the current class + GenericShadowChecker checker(&cache, THREAD, target->name(), + holder, selected_family->descriptor(), super_class); + checker.run(current_class); + + if (checker.found_shadow()) { +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr(" Only candidate found was shadowed."); + } +#endif // ndef PRODUCT + THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(), + "Accessible default method not found", NULL); + } else { + return target; + } } else { assert(selected_family->throws_exception(), "must have target or throw"); THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(), @@ -1075,6 +1263,71 @@ Method* DefaultMethods::find_super_default( } } +// This is called during linktime when we find an invokespecial call that +// refers to a direct superinterface. It indicates that we should find the +// default method in the hierarchy of that superinterface, and if that method +// would have been a candidate from the point of view of 'this' class, then we +// return that method. +// This logic assumes that the super is a direct superclass of the caller +Method* DefaultMethods::find_super_default( + Klass* cls, Klass* super, Symbol* method_name, Symbol* sig, TRAPS) { + + ResourceMark rm(THREAD); + + assert(cls != NULL && super != NULL, "Need real classes"); + + InstanceKlass* current_class = InstanceKlass::cast(cls); + InstanceKlass* super_class = InstanceKlass::cast(super); + + // Keep entire hierarchy alive for the duration of the computation + KeepAliveRegistrar keepAlive(THREAD); + KeepAliveVisitor loadKeepAlive(&keepAlive); + loadKeepAlive.run(current_class); // get hierarchy from current class + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr("Finding super default method %s.%s%s from %s", + super_class->name()->as_C_string(), + method_name->as_C_string(), sig->as_C_string(), + current_class->name()->as_C_string()); + } +#endif // ndef PRODUCT + + assert(super_class->is_interface(), "only call for default methods"); + + Method* target = NULL; + if (ParseGenericDefaults) { + target = find_generic_super_default(current_class, super_class, + method_name, sig, CHECK_NULL); + } else { + target = find_erased_super_default(current_class, super_class, + method_name, sig, CHECK_NULL); + } + +#ifndef PRODUCT + if (target != NULL) { + if (TraceDefaultMethods) { + tty->print(" Returning "); + print_method(tty, target, true); + tty->print_cr(""); + } + } +#endif // ndef PRODUCT + return target; +} + +#ifndef PRODUCT +// Return true is broad type is a covariant return of narrow type +static bool covariant_return_type(BasicType narrow, BasicType broad) { + if (narrow == broad) { + return true; + } + if (broad == T_OBJECT) { + return true; + } + return false; +} +#endif // ndef PRODUCT static int assemble_redirect( BytecodeConstantPool* cp, BytecodeBuffer* buffer, @@ -1103,7 +1356,7 @@ static int assemble_redirect( out.next(); } assert(out.at_return_type(), "Parameter counts do not match"); - assert(in.type() == out.type(), "Return types are not compatible"); + assert(covariant_return_type(out.type(), in.type()), "Return types are not compatible"); if (parameter_count == 1 && (in.type() == T_LONG || in.type() == T_DOUBLE)) { ++parameter_count; // need room for return value @@ -1144,10 +1397,15 @@ static Method* new_method( Symbol* sig, AccessFlags flags, int max_stack, int params, ConstMethod::MethodType mt, TRAPS) { - address code_start = static_cast
(bytecodes->adr_at(0)); - int code_length = bytecodes->length(); + address code_start = 0; + int code_length = 0; InlineTableSizes sizes; + if (bytecodes != NULL && bytecodes->length() > 0) { + code_start = static_cast
(bytecodes->adr_at(0)); + code_length = bytecodes->length(); + } + Method* m = Method::allocate(cp->pool_holder()->class_loader_data(), code_length, flags, &sizes, mt, CHECK_NULL); diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index 81f5705816c..899d3ba48a3 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -234,6 +234,7 @@ class java_lang_Class : AllStatic { static GrowableArray* _fixup_mirror_list; static void set_init_lock(oop java_class, oop init_lock); + static void set_protection_domain(oop java_class, oop protection_domain); public: static void compute_offsets(); @@ -272,7 +273,6 @@ class java_lang_Class : AllStatic { // Support for embedded per-class oops static oop protection_domain(oop java_class); - static void set_protection_domain(oop java_class, oop protection_domain); static oop init_lock(oop java_class); static objArrayOop signers(oop java_class); static void set_signers(oop java_class, objArrayOop signers); diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index 7ec43bd7e86..df370237167 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -53,8 +53,6 @@ template(java_lang_Object, "java/lang/Object") \ template(java_lang_Class, "java/lang/Class") \ template(java_lang_String, "java/lang/String") \ - template(java_lang_StringValue, "java/lang/StringValue") \ - template(java_lang_StringCache, "java/lang/StringValue$StringCache") \ template(java_lang_Thread, "java/lang/Thread") \ template(java_lang_ThreadGroup, "java/lang/ThreadGroup") \ template(java_lang_Cloneable, "java/lang/Cloneable") \ @@ -106,7 +104,6 @@ template(java_util_Vector, "java/util/Vector") \ template(java_util_AbstractList, "java/util/AbstractList") \ template(java_util_Hashtable, "java/util/Hashtable") \ - template(java_util_HashMap, "java/util/HashMap") \ template(java_lang_Compiler, "java/lang/Compiler") \ template(sun_misc_Signal, "sun/misc/Signal") \ template(java_lang_AssertionStatusDirectives, "java/lang/AssertionStatusDirectives") \ @@ -367,8 +364,6 @@ template(offset_name, "offset") \ template(count_name, "count") \ template(hash_name, "hash") \ - template(frontCacheEnabled_name, "frontCacheEnabled") \ - template(stringCacheEnabled_name, "stringCacheEnabled") \ template(numberOfLeadingZeros_name, "numberOfLeadingZeros") \ template(numberOfTrailingZeros_name, "numberOfTrailingZeros") \ template(bitCount_name, "bitCount") \ diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp index d91f907a2c0..b87efd7bc2a 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp @@ -2017,12 +2017,6 @@ oop_since_save_marks_iterate##nv_suffix(OopClosureType* blk) { \ ALL_SINCE_SAVE_MARKS_CLOSURES(CFLS_OOP_SINCE_SAVE_MARKS_DEFN) - -void CompactibleFreeListSpace::object_iterate_since_last_GC(ObjectClosure* cl) { - // ugghh... how would one do this efficiently for a non-contiguous space? - guarantee(false, "NYI"); -} - bool CompactibleFreeListSpace::linearAllocationWouldFail() const { return _smallLinearAllocBlock._word_size == 0; } diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp index 23c95897cfa..74c97e8df9d 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp @@ -396,7 +396,6 @@ class CompactibleFreeListSpace: public CompactibleSpace { // iteration support for promotion void save_marks(); bool no_allocs_since_save_marks(); - void object_iterate_since_last_GC(ObjectClosure* cl); // iteration support for sweeping void save_sweep_limit() { diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 85c7b73172e..ced79cbcf69 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -3129,26 +3129,6 @@ oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl) { \ ALL_SINCE_SAVE_MARKS_CLOSURES(CMS_SINCE_SAVE_MARKS_DEFN) -void -ConcurrentMarkSweepGeneration::object_iterate_since_last_GC(ObjectClosure* blk) -{ - // Not currently implemented; need to do the following. -- ysr. - // dld -- I think that is used for some sort of allocation profiler. So it - // really means the objects allocated by the mutator since the last - // GC. We could potentially implement this cheaply by recording only - // the direct allocations in a side data structure. - // - // I think we probably ought not to be required to support these - // iterations at any arbitrary point; I think there ought to be some - // call to enable/disable allocation profiling in a generation/space, - // and the iterator ought to return the objects allocated in the - // gen/space since the enable call, or the last iterator call (which - // will probably be at a GC.) That way, for gens like CM&S that would - // require some extra data structure to support this, we only pay the - // cost when it's in use... - cmsSpace()->object_iterate_since_last_GC(blk); -} - void ConcurrentMarkSweepGeneration::younger_refs_iterate(OopsInGenClosure* cl) { cl->set_generation(this); diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index bb485f4a83e..3806b896d26 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -1273,7 +1273,6 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { // Iteration support and related enquiries void save_marks(); bool no_allocs_since_save_marks(); - void object_iterate_since_last_GC(ObjectClosure* cl); void younger_refs_iterate(OopsInGenClosure* cl); // Iteration support specific to CMS generations diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index db8f863ad25..bdd3027b995 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -54,7 +54,6 @@ #include "memory/referenceProcessor.hpp" #include "oops/oop.inline.hpp" #include "oops/oop.pcgc.inline.hpp" -#include "runtime/aprofiler.hpp" #include "runtime/vmThread.hpp" size_t G1CollectedHeap::_humongous_object_threshold_in_words = 0; @@ -2665,11 +2664,6 @@ void G1CollectedHeap::object_iterate(ObjectClosure* cl) { heap_region_iterate(&blk); } -void G1CollectedHeap::object_iterate_since_last_GC(ObjectClosure* cl) { - // FIXME: is this right? - guarantee(false, "object_iterate_since_last_GC not supported by G1 heap"); -} - // Calls a SpaceClosure on a HeapRegion. class SpaceClosureRegionClosure: public HeapRegionClosure { @@ -3598,8 +3592,6 @@ G1CollectedHeap* G1CollectedHeap::heap() { void G1CollectedHeap::gc_prologue(bool full /* Ignored */) { // always_do_update_barrier = false; assert(InlineCacheBuffer::is_empty(), "should have cleaned up ICBuffer"); - // Call allocation profiler - AllocationProfiler::iterate_since_last_gc(); // Fill TLAB's and such ensure_parsability(true); } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 6843d13f736..9b52304c996 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -1360,11 +1360,6 @@ public: object_iterate(cl); } - // Iterate over all objects allocated since the last collection, calling - // "cl.do_object" on each. The heap must have been initialized properly - // to support this function, or else this call will fail. - virtual void object_iterate_since_last_GC(ObjectClosure* cl); - // Iterate over all spaces in use in the heap, in ascending address order. virtual void space_iterate(SpaceClosure* cl); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 3bd04281955..4c6d2bbf877 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -873,7 +873,7 @@ bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc size_t alloc_byte_size = alloc_word_size * HeapWordSize; if ((cur_used_bytes + alloc_byte_size) > marking_initiating_used_threshold) { - if (gcs_are_young()) { + if (gcs_are_young() && !_last_young_gc) { ergo_verbose5(ErgoConcCycles, "request concurrent cycle initiation", ergo_format_reason("occupancy higher than threshold") @@ -931,7 +931,7 @@ void G1CollectorPolicy::record_collection_pause_end(double pause_time_ms, Evacua last_pause_included_initial_mark = during_initial_mark_pause(); if (last_pause_included_initial_mark) { record_concurrent_mark_init_end(0.0); - } else if (!_last_young_gc && need_to_start_conc_mark("end of GC")) { + } else if (need_to_start_conc_mark("end of GC")) { // Note: this might have already been set, if during the last // pause we decided to start a cycle but at the beginning of // this pause we decided to postpone it. That's OK. diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp index adde08f2177..74aabc1298d 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1MarkSweep.cpp @@ -43,7 +43,6 @@ #include "oops/instanceRefKlass.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiExport.hpp" -#include "runtime/aprofiler.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/fprofiler.hpp" #include "runtime/synchronizer.hpp" diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp index 6c53670425b..555e2ed9cf8 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp @@ -23,12 +23,14 @@ */ #include "precompiled.hpp" +#include "gc_implementation/shared/copyFailedInfo.hpp" #include "gc_implementation/shared/gcHeapSummary.hpp" #include "gc_implementation/shared/gcTimer.hpp" #include "gc_implementation/shared/gcTrace.hpp" -#include "gc_implementation/shared/copyFailedInfo.hpp" +#include "gc_implementation/shared/objectCountEventSender.hpp" #include "memory/heapInspection.hpp" #include "memory/referenceProcessorStats.hpp" +#include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" #if INCLUDE_ALL_GCS @@ -38,7 +40,7 @@ #define assert_unset_gc_id() assert(_shared_gc_info.id() == SharedGCInfo::UNSET_GCID, "GC already started?") #define assert_set_gc_id() assert(_shared_gc_info.id() != SharedGCInfo::UNSET_GCID, "GC not started?") -static jlong GCTracer_next_gc_id = 0; +static GCId GCTracer_next_gc_id = 0; static GCId create_new_gc_id() { return GCTracer_next_gc_id++; } @@ -91,26 +93,38 @@ void GCTracer::report_gc_reference_stats(const ReferenceProcessorStats& rps) con } #if INCLUDE_SERVICES -void ObjectCountEventSenderClosure::do_cinfo(KlassInfoEntry* entry) { - if (should_send_event(entry)) { - send_event(entry); +class ObjectCountEventSenderClosure : public KlassInfoClosure { + const GCId _gc_id; + const double _size_threshold_percentage; + const size_t _total_size_in_words; + const jlong _timestamp; + + public: + ObjectCountEventSenderClosure(GCId gc_id, size_t total_size_in_words, jlong timestamp) : + _gc_id(gc_id), + _size_threshold_percentage(ObjectCountCutOffPercent / 100), + _total_size_in_words(total_size_in_words), + _timestamp(timestamp) + {} + + virtual void do_cinfo(KlassInfoEntry* entry) { + if (should_send_event(entry)) { + ObjectCountEventSender::send(entry, _gc_id, _timestamp); + } } -} -void ObjectCountEventSenderClosure::send_event(KlassInfoEntry* entry) { - _gc_tracer->send_object_count_after_gc_event(entry->klass(), entry->count(), - entry->words() * BytesPerWord); -} - -bool ObjectCountEventSenderClosure::should_send_event(KlassInfoEntry* entry) const { - double percentage_of_heap = ((double) entry->words()) / _total_size_in_words; - return percentage_of_heap > _size_threshold_percentage; -} + private: + bool should_send_event(const KlassInfoEntry* entry) const { + double percentage_of_heap = ((double) entry->words()) / _total_size_in_words; + return percentage_of_heap >= _size_threshold_percentage; + } +}; void GCTracer::report_object_count_after_gc(BoolObjectClosure* is_alive_cl) { assert_set_gc_id(); + assert(is_alive_cl != NULL, "Must supply function to check liveness"); - if (should_send_object_count_after_gc_event()) { + if (ObjectCountEventSender::should_send_event()) { ResourceMark rm; KlassInfoTable cit(false); @@ -118,12 +132,13 @@ void GCTracer::report_object_count_after_gc(BoolObjectClosure* is_alive_cl) { HeapInspection hi(false, false, false, NULL); hi.populate_table(&cit, is_alive_cl); - ObjectCountEventSenderClosure event_sender(this, cit.size_of_instances_in_words()); + jlong timestamp = os::elapsed_counter(); + ObjectCountEventSenderClosure event_sender(_shared_gc_info.id(), cit.size_of_instances_in_words(), timestamp); cit.iterate(&event_sender); } } } -#endif +#endif // INCLUDE_SERVICES void GCTracer::report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary, const MetaspaceSummary& meta_space_summary) const { assert_set_gc_id(); diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp index 29ee55b685d..c157d86e7a3 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp +++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp @@ -30,7 +30,6 @@ #include "gc_implementation/shared/gcWhen.hpp" #include "gc_implementation/shared/copyFailedInfo.hpp" #include "memory/allocation.hpp" -#include "memory/klassInfoClosure.hpp" #include "memory/referenceType.hpp" #if INCLUDE_ALL_GCS #include "gc_implementation/g1/g1YCTypes.hpp" @@ -113,7 +112,6 @@ class G1YoungGCInfo VALUE_OBJ_CLASS_SPEC { #endif // INCLUDE_ALL_GCS class GCTracer : public ResourceObj { - friend class ObjectCountEventSenderClosure; protected: SharedGCInfo _shared_gc_info; @@ -123,7 +121,6 @@ class GCTracer : public ResourceObj { void report_gc_heap_summary(GCWhen::Type when, const GCHeapSummary& heap_summary, const MetaspaceSummary& meta_space_summary) const; void report_gc_reference_stats(const ReferenceProcessorStats& rp) const; void report_object_count_after_gc(BoolObjectClosure* object_filter) NOT_SERVICES_RETURN; - bool has_reported_gc_start() const; protected: @@ -137,25 +134,6 @@ class GCTracer : public ResourceObj { void send_meta_space_summary_event(GCWhen::Type when, const MetaspaceSummary& meta_space_summary) const; void send_reference_stats_event(ReferenceType type, size_t count) const; void send_phase_events(TimePartitions* time_partitions) const; - void send_object_count_after_gc_event(Klass* klass, jlong count, julong total_size) const NOT_SERVICES_RETURN; - bool should_send_object_count_after_gc_event() const; -}; - -class ObjectCountEventSenderClosure : public KlassInfoClosure { - GCTracer* _gc_tracer; - const double _size_threshold_percentage; - const size_t _total_size_in_words; - public: - ObjectCountEventSenderClosure(GCTracer* gc_tracer, size_t total_size_in_words) : - _gc_tracer(gc_tracer), - _size_threshold_percentage(ObjectCountCutOffPercent / 100), - _total_size_in_words(total_size_in_words) - {} - virtual void do_cinfo(KlassInfoEntry* entry); - protected: - virtual void send_event(KlassInfoEntry* entry); - private: - bool should_send_event(KlassInfoEntry* entry) const; }; class YoungGCTracer : public GCTracer { diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp index 4af7e3c2fbf..da0c3856dd6 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp @@ -123,27 +123,6 @@ void OldGCTracer::send_concurrent_mode_failure_event() { } } -#if INCLUDE_SERVICES -void GCTracer::send_object_count_after_gc_event(Klass* klass, jlong count, julong total_size) const { - EventObjectCountAfterGC e; - if (e.should_commit()) { - e.set_gcId(_shared_gc_info.id()); - e.set_class(klass); - e.set_count(count); - e.set_totalSize(total_size); - e.commit(); - } -} -#endif - -bool GCTracer::should_send_object_count_after_gc_event() const { -#if INCLUDE_TRACE - return Tracing::is_event_enabled(EventObjectCountAfterGC::eventId); -#else - return false; -#endif -} - #if INCLUDE_ALL_GCS void G1NewTracer::send_g1_young_gc_event() { EventGCG1GarbageCollection e(UNTIMED); diff --git a/hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp b/hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp new file mode 100644 index 00000000000..cf95bdd505e --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013, 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 "precompiled.hpp" +#include "gc_implementation/shared/objectCountEventSender.hpp" +#include "memory/heapInspection.hpp" +#include "trace/tracing.hpp" +#include "utilities/globalDefinitions.hpp" + +#if INCLUDE_SERVICES + +void ObjectCountEventSender::send(const KlassInfoEntry* entry, GCId gc_id, jlong timestamp) { + assert(Tracing::is_event_enabled(EventObjectCountAfterGC::eventId), + "Only call this method if the event is enabled"); + + EventObjectCountAfterGC event(UNTIMED); + event.set_gcId(gc_id); + event.set_class(entry->klass()); + event.set_count(entry->count()); + event.set_totalSize(entry->words() * BytesPerWord); + event.set_endtime(timestamp); + event.commit(); +} + +bool ObjectCountEventSender::should_send_event() { +#if INCLUDE_TRACE + return Tracing::is_event_enabled(EventObjectCountAfterGC::eventId); +#else + return false; +#endif // INCLUDE_TRACE +} + +#endif // INCLUDE_SERVICES diff --git a/hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp b/hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp new file mode 100644 index 00000000000..b83e1fa737d --- /dev/null +++ b/hotspot/src/share/vm/gc_implementation/shared/objectCountEventSender.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, 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. + * + */ + +#ifndef SHARE_VM_OBJECT_COUNT_EVENT_SENDER_HPP +#define SHARE_VM_OBJECT_COUNT_EVENT_SENDER_HPP + +#include "gc_implementation/shared/gcTrace.hpp" +#include "memory/allocation.hpp" +#include "utilities/macros.hpp" + +#if INCLUDE_SERVICES + +class KlassInfoEntry; + +class ObjectCountEventSender : public AllStatic { + public: + static void send(const KlassInfoEntry* entry, GCId gc_id, jlong timestamp); + static bool should_send_event(); +}; + +#endif // INCLUDE_SERVICES + +#endif // SHARE_VM_OBJECT_COUNT_EVENT_SENDER diff --git a/hotspot/src/share/vm/gc_interface/collectedHeap.cpp b/hotspot/src/share/vm/gc_interface/collectedHeap.cpp index 4c6c026e74b..dfc963c3bc4 100644 --- a/hotspot/src/share/vm/gc_interface/collectedHeap.cpp +++ b/hotspot/src/share/vm/gc_interface/collectedHeap.cpp @@ -85,16 +85,16 @@ GCHeapSummary CollectedHeap::create_heap_summary() { MetaspaceSummary CollectedHeap::create_metaspace_summary() { const MetaspaceSizes meta_space( - 0, /*MetaspaceAux::capacity_in_bytes(),*/ - 0, /*MetaspaceAux::used_in_bytes(),*/ + MetaspaceAux::allocated_capacity_bytes(), + MetaspaceAux::allocated_used_bytes(), MetaspaceAux::reserved_in_bytes()); const MetaspaceSizes data_space( - 0, /*MetaspaceAux::capacity_in_bytes(Metaspace::NonClassType),*/ - 0, /*MetaspaceAux::used_in_bytes(Metaspace::NonClassType),*/ + MetaspaceAux::allocated_capacity_bytes(Metaspace::NonClassType), + MetaspaceAux::allocated_used_bytes(Metaspace::NonClassType), MetaspaceAux::reserved_in_bytes(Metaspace::NonClassType)); const MetaspaceSizes class_space( - 0, /*MetaspaceAux::capacity_in_bytes(Metaspace::ClassType),*/ - 0, /*MetaspaceAux::used_in_bytes(Metaspace::ClassType),*/ + MetaspaceAux::allocated_capacity_bytes(Metaspace::ClassType), + MetaspaceAux::allocated_used_bytes(Metaspace::ClassType), MetaspaceAux::reserved_in_bytes(Metaspace::ClassType)); return MetaspaceSummary(meta_space, data_space, class_space); diff --git a/hotspot/src/share/vm/memory/allocation.cpp b/hotspot/src/share/vm/memory/allocation.cpp index c45dab1c4c5..6a80c47385e 100644 --- a/hotspot/src/share/vm/memory/allocation.cpp +++ b/hotspot/src/share/vm/memory/allocation.cpp @@ -236,10 +236,11 @@ class ChunkPool: public CHeapObj { size_t _num_used; // number of chunks currently checked out const size_t _size; // size of each chunk (must be uniform) - // Our three static pools + // Our four static pools static ChunkPool* _large_pool; static ChunkPool* _medium_pool; static ChunkPool* _small_pool; + static ChunkPool* _tiny_pool; // return first element or null void* get_first() { @@ -319,15 +320,18 @@ class ChunkPool: public CHeapObj { static ChunkPool* large_pool() { assert(_large_pool != NULL, "must be initialized"); return _large_pool; } static ChunkPool* medium_pool() { assert(_medium_pool != NULL, "must be initialized"); return _medium_pool; } static ChunkPool* small_pool() { assert(_small_pool != NULL, "must be initialized"); return _small_pool; } + static ChunkPool* tiny_pool() { assert(_tiny_pool != NULL, "must be initialized"); return _tiny_pool; } static void initialize() { _large_pool = new ChunkPool(Chunk::size + Chunk::aligned_overhead_size()); _medium_pool = new ChunkPool(Chunk::medium_size + Chunk::aligned_overhead_size()); _small_pool = new ChunkPool(Chunk::init_size + Chunk::aligned_overhead_size()); + _tiny_pool = new ChunkPool(Chunk::tiny_size + Chunk::aligned_overhead_size()); } static void clean() { enum { BlocksToKeep = 5 }; + _tiny_pool->free_all_but(BlocksToKeep); _small_pool->free_all_but(BlocksToKeep); _medium_pool->free_all_but(BlocksToKeep); _large_pool->free_all_but(BlocksToKeep); @@ -337,6 +341,7 @@ class ChunkPool: public CHeapObj { ChunkPool* ChunkPool::_large_pool = NULL; ChunkPool* ChunkPool::_medium_pool = NULL; ChunkPool* ChunkPool::_small_pool = NULL; +ChunkPool* ChunkPool::_tiny_pool = NULL; void chunkpool_init() { ChunkPool::initialize(); @@ -376,6 +381,7 @@ void* Chunk::operator new (size_t requested_size, AllocFailType alloc_failmode, case Chunk::size: return ChunkPool::large_pool()->allocate(bytes, alloc_failmode); case Chunk::medium_size: return ChunkPool::medium_pool()->allocate(bytes, alloc_failmode); case Chunk::init_size: return ChunkPool::small_pool()->allocate(bytes, alloc_failmode); + case Chunk::tiny_size: return ChunkPool::tiny_pool()->allocate(bytes, alloc_failmode); default: { void* p = os::malloc(bytes, mtChunk, CALLER_PC); if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) { @@ -392,6 +398,7 @@ void Chunk::operator delete(void* p) { case Chunk::size: ChunkPool::large_pool()->free(c); break; case Chunk::medium_size: ChunkPool::medium_pool()->free(c); break; case Chunk::init_size: ChunkPool::small_pool()->free(c); break; + case Chunk::tiny_size: ChunkPool::tiny_pool()->free(c); break; default: os::free(c, mtChunk); } } diff --git a/hotspot/src/share/vm/memory/allocation.hpp b/hotspot/src/share/vm/memory/allocation.hpp index 17654f06984..aa916660d8b 100644 --- a/hotspot/src/share/vm/memory/allocation.hpp +++ b/hotspot/src/share/vm/memory/allocation.hpp @@ -353,7 +353,8 @@ class Chunk: CHeapObj { slack = 20, // suspected sizeof(Chunk) + internal malloc headers #endif - init_size = 1*K - slack, // Size of first chunk + tiny_size = 256 - slack, // Size of first chunk (tiny) + init_size = 1*K - slack, // Size of first chunk (normal aka small) medium_size= 10*K - slack, // Size of medium-sized chunk size = 32*K - slack, // Default size of an Arena chunk (following the first) non_pool_size = init_size + 32 // An initial size which is not one of above diff --git a/hotspot/src/share/vm/memory/defNewGeneration.cpp b/hotspot/src/share/vm/memory/defNewGeneration.cpp index 88afd70a739..177569294af 100644 --- a/hotspot/src/share/vm/memory/defNewGeneration.cpp +++ b/hotspot/src/share/vm/memory/defNewGeneration.cpp @@ -450,11 +450,6 @@ void DefNewGeneration::compute_new_size() { } } -void DefNewGeneration::object_iterate_since_last_GC(ObjectClosure* cl) { - // $$$ This may be wrong in case of "scavenge failure"? - eden()->object_iterate(cl); -} - void DefNewGeneration::younger_refs_iterate(OopsInGenClosure* cl) { assert(false, "NYI -- are you sure you want to call this?"); } diff --git a/hotspot/src/share/vm/memory/defNewGeneration.hpp b/hotspot/src/share/vm/memory/defNewGeneration.hpp index c538e0a4f35..0623d446ad3 100644 --- a/hotspot/src/share/vm/memory/defNewGeneration.hpp +++ b/hotspot/src/share/vm/memory/defNewGeneration.hpp @@ -252,7 +252,6 @@ protected: // Iteration void object_iterate(ObjectClosure* blk); - void object_iterate_since_last_GC(ObjectClosure* cl); void younger_refs_iterate(OopsInGenClosure* cl); diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.cpp b/hotspot/src/share/vm/memory/genCollectedHeap.cpp index a74533d4321..ebdb77f6734 100644 --- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp +++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp @@ -42,7 +42,6 @@ #include "memory/space.hpp" #include "oops/oop.inline.hpp" #include "oops/oop.inline2.hpp" -#include "runtime/aprofiler.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/fprofiler.hpp" #include "runtime/handles.hpp" @@ -873,12 +872,6 @@ void GenCollectedHeap::safe_object_iterate(ObjectClosure* cl) { } } -void GenCollectedHeap::object_iterate_since_last_GC(ObjectClosure* cl) { - for (int i = 0; i < _n_gens; i++) { - _gens[i]->object_iterate_since_last_GC(cl); - } -} - Space* GenCollectedHeap::space_containing(const void* addr) const { for (int i = 0; i < _n_gens; i++) { Space* res = _gens[i]->space_containing(addr); @@ -1186,8 +1179,6 @@ void GenCollectedHeap::gc_prologue(bool full) { CollectedHeap::accumulate_statistics_all_tlabs(); ensure_parsability(true); // retire TLABs - // Call allocation profiler - AllocationProfiler::iterate_since_last_gc(); // Walk generations GenGCPrologueClosure blk(full); generation_iterate(&blk, false); // not old-to-young. diff --git a/hotspot/src/share/vm/memory/genCollectedHeap.hpp b/hotspot/src/share/vm/memory/genCollectedHeap.hpp index 783cd372d7c..43338c4da46 100644 --- a/hotspot/src/share/vm/memory/genCollectedHeap.hpp +++ b/hotspot/src/share/vm/memory/genCollectedHeap.hpp @@ -222,7 +222,6 @@ public: void oop_iterate(MemRegion mr, ExtendedOopClosure* cl); void object_iterate(ObjectClosure* cl); void safe_object_iterate(ObjectClosure* cl); - void object_iterate_since_last_GC(ObjectClosure* cl); Space* space_containing(const void* addr) const; // A CollectedHeap is divided into a dense sequence of "blocks"; that is, diff --git a/hotspot/src/share/vm/memory/generation.cpp b/hotspot/src/share/vm/memory/generation.cpp index 9fa46b34af4..f9c986fa7a0 100644 --- a/hotspot/src/share/vm/memory/generation.cpp +++ b/hotspot/src/share/vm/memory/generation.cpp @@ -811,16 +811,6 @@ void OneContigSpaceCardGeneration::space_iterate(SpaceClosure* blk, blk->do_space(_the_space); } -void OneContigSpaceCardGeneration::object_iterate_since_last_GC(ObjectClosure* blk) { - // Deal with delayed initialization of _the_space, - // and lack of initialization of _last_gc. - if (_last_gc.space() == NULL) { - assert(the_space() != NULL, "shouldn't be NULL"); - _last_gc = the_space()->bottom_mark(); - } - the_space()->object_iterate_from(_last_gc, blk); -} - void OneContigSpaceCardGeneration::younger_refs_iterate(OopsInGenClosure* blk) { blk->set_generation(this); younger_refs_in_space_iterate(_the_space, blk); diff --git a/hotspot/src/share/vm/memory/generation.hpp b/hotspot/src/share/vm/memory/generation.hpp index feb4fde18af..44d641feedd 100644 --- a/hotspot/src/share/vm/memory/generation.hpp +++ b/hotspot/src/share/vm/memory/generation.hpp @@ -551,12 +551,6 @@ class Generation: public CHeapObj { // the heap. This defaults to object_iterate() unless overridden. virtual void safe_object_iterate(ObjectClosure* cl); - // Iterate over all objects allocated in the generation since the last - // collection, calling "cl.do_object" on each. The generation must have - // been initialized properly to support this function, or else this call - // will fail. - virtual void object_iterate_since_last_GC(ObjectClosure* cl) = 0; - // Apply "cl->do_oop" to (the address of) all and only all the ref fields // in the current generation that contain pointers to objects in younger // generations. Objects allocated since the last "save_marks" call are @@ -724,7 +718,6 @@ class OneContigSpaceCardGeneration: public CardGeneration { // Iteration void object_iterate(ObjectClosure* blk); void space_iterate(SpaceClosure* blk, bool usedOnly = false); - void object_iterate_since_last_GC(ObjectClosure* cl); void younger_refs_iterate(OopsInGenClosure* blk); diff --git a/hotspot/src/share/vm/memory/heapInspection.hpp b/hotspot/src/share/vm/memory/heapInspection.hpp index c286e48658f..09558b0a2a9 100644 --- a/hotspot/src/share/vm/memory/heapInspection.hpp +++ b/hotspot/src/share/vm/memory/heapInspection.hpp @@ -26,7 +26,6 @@ #define SHARE_VM_MEMORY_HEAPINSPECTION_HPP #include "memory/allocation.inline.hpp" -#include "memory/klassInfoClosure.hpp" #include "oops/oop.inline.hpp" #include "oops/annotations.hpp" #include "utilities/macros.hpp" @@ -204,6 +203,12 @@ class KlassInfoEntry: public CHeapObj { const char* name() const; }; +class KlassInfoClosure : public StackObj { + public: + // Called for each KlassInfoEntry. + virtual void do_cinfo(KlassInfoEntry* cie) = 0; +}; + class KlassInfoBucket: public CHeapObj { private: KlassInfoEntry* _list; diff --git a/hotspot/src/share/vm/memory/resourceArea.hpp b/hotspot/src/share/vm/memory/resourceArea.hpp index 0699334a5e6..1357081fd67 100644 --- a/hotspot/src/share/vm/memory/resourceArea.hpp +++ b/hotspot/src/share/vm/memory/resourceArea.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, 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 @@ -83,6 +83,10 @@ protected: Chunk *_chunk; // saved arena chunk char *_hwm, *_max; size_t _size_in_bytes; +#ifdef ASSERT + Thread* _thread; + ResourceMark* _previous_resource_mark; +#endif //ASSERT void initialize(Thread *thread) { _area = thread->resource_area(); @@ -92,6 +96,11 @@ protected: _size_in_bytes = _area->size_in_bytes(); debug_only(_area->_nesting++;) assert( _area->_nesting > 0, "must stack allocate RMs" ); +#ifdef ASSERT + _thread = thread; + _previous_resource_mark = thread->current_resource_mark(); + thread->set_current_resource_mark(this); +#endif // ASSERT } public: @@ -111,6 +120,17 @@ protected: _size_in_bytes = r->_size_in_bytes; debug_only(_area->_nesting++;) assert( _area->_nesting > 0, "must stack allocate RMs" ); +#ifdef ASSERT + Thread* thread = ThreadLocalStorage::thread(); + if (thread != NULL) { + _thread = thread; + _previous_resource_mark = thread->current_resource_mark(); + thread->set_current_resource_mark(this); + } else { + _thread = NULL; + _previous_resource_mark = NULL; + } +#endif // ASSERT } void reset_to_mark() { @@ -137,6 +157,11 @@ protected: assert( _area->_nesting > 0, "must stack allocate RMs" ); debug_only(_area->_nesting--;) reset_to_mark(); +#ifdef ASSERT + if (_thread != NULL) { + _thread->set_current_resource_mark(_previous_resource_mark); + } +#endif // ASSERT } diff --git a/hotspot/src/share/vm/memory/sharedHeap.hpp b/hotspot/src/share/vm/memory/sharedHeap.hpp index b13bf15b846..cd810c036d4 100644 --- a/hotspot/src/share/vm/memory/sharedHeap.hpp +++ b/hotspot/src/share/vm/memory/sharedHeap.hpp @@ -166,11 +166,6 @@ public: // Same as above, restricted to a memory region. virtual void oop_iterate(MemRegion mr, ExtendedOopClosure* cl) = 0; - // Iterate over all objects allocated since the last collection, calling - // "cl->do_object" on each. The heap must have been initialized properly - // to support this function, or else this call will fail. - virtual void object_iterate_since_last_GC(ObjectClosure* cl) = 0; - // Iterate over all spaces in use in the heap, in an undefined order. virtual void space_iterate(SpaceClosure* cl) = 0; diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index 3514886d0d9..511610bd3bf 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -52,7 +52,6 @@ #include "oops/oop.inline.hpp" #include "oops/typeArrayKlass.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp" -#include "runtime/aprofiler.hpp" #include "runtime/arguments.hpp" #include "runtime/deoptimization.hpp" #include "runtime/fprofiler.hpp" diff --git a/hotspot/src/share/vm/oops/arrayKlass.cpp b/hotspot/src/share/vm/oops/arrayKlass.cpp index ef1c20e972b..6e04c3ac145 100644 --- a/hotspot/src/share/vm/oops/arrayKlass.cpp +++ b/hotspot/src/share/vm/oops/arrayKlass.cpp @@ -71,7 +71,6 @@ Method* ArrayKlass::uncached_lookup_method(Symbol* name, Symbol* signature) cons } ArrayKlass::ArrayKlass(Symbol* name) { - set_alloc_size(0); set_name(name); set_super(Universe::is_bootstrapping() ? (Klass*)NULL : SystemDictionary::Object_klass()); @@ -161,12 +160,6 @@ void ArrayKlass::array_klasses_do(void f(Klass* k)) { } } - -void ArrayKlass::with_array_klasses_do(void f(Klass* k)) { - array_klasses_do(f); -} - - // GC support void ArrayKlass::oops_do(OopClosure* cl) { diff --git a/hotspot/src/share/vm/oops/arrayKlass.hpp b/hotspot/src/share/vm/oops/arrayKlass.hpp index acf920e16d1..4b06f1c0ed6 100644 --- a/hotspot/src/share/vm/oops/arrayKlass.hpp +++ b/hotspot/src/share/vm/oops/arrayKlass.hpp @@ -39,7 +39,6 @@ class ArrayKlass: public Klass { Klass* volatile _higher_dimension; // Refers the (n+1)'th-dimensional array (if present). Klass* volatile _lower_dimension; // Refers the (n-1)'th-dimensional array (if present). int _vtable_len; // size of vtable for this klass - juint _alloc_size; // allocation profiling support oop _component_mirror; // component type, as a java/lang/Class protected: @@ -65,10 +64,6 @@ class ArrayKlass: public Klass { void set_lower_dimension(Klass* k) { _lower_dimension = k; } Klass** adr_lower_dimension() { return (Klass**)&this->_lower_dimension;} - // Allocation profiling support - juint alloc_size() const { return _alloc_size; } - void set_alloc_size(juint n) { _alloc_size = n; } - // offset of first element, including any padding for the sake of alignment int array_header_in_bytes() const { return layout_helper_header_size(layout_helper()); } int log2_element_size() const { return layout_helper_log2_element_size(layout_helper()); } @@ -126,7 +121,6 @@ class ArrayKlass: public Klass { // Iterators void array_klasses_do(void f(Klass* k)); void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); - void with_array_klasses_do(void f(Klass* k)); // GC support virtual void oops_do(OopClosure* cl); diff --git a/hotspot/src/share/vm/oops/cpCache.hpp b/hotspot/src/share/vm/oops/cpCache.hpp index 27ca7980c07..c15b4a54bd6 100644 --- a/hotspot/src/share/vm/oops/cpCache.hpp +++ b/hotspot/src/share/vm/oops/cpCache.hpp @@ -140,8 +140,15 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { _f1 = f1; } void release_set_f1(Metadata* f1); - void set_f2(intx f2) { assert(_f2 == 0 || _f2 == f2, "illegal field change"); _f2 = f2; } - void set_f2_as_vfinal_method(Method* f2) { assert(_f2 == 0 || _f2 == (intptr_t) f2, "illegal field change"); assert(is_vfinal(), "flags must be set"); _f2 = (intptr_t) f2; } + void set_f2(intx f2) { + intx existing_f2 = _f2; // read once + assert(existing_f2 == 0 || existing_f2 == f2, "illegal field change"); + _f2 = f2; + } + void set_f2_as_vfinal_method(Method* f2) { + assert(is_vfinal(), "flags must be set"); + set_f2((intx)f2); + } int make_flags(TosState state, int option_bits, int field_index_or_method_params); void set_flags(intx flags) { _flags = flags; } bool init_flags_atomic(intx flags); diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index 886746d6bb6..a2684d84bb1 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -48,6 +48,7 @@ #include "oops/symbol.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp" +#include "prims/jvmtiRedefineClasses.hpp" #include "prims/methodComparator.hpp" #include "runtime/fieldDescriptor.hpp" #include "runtime/handles.inline.hpp" @@ -291,7 +292,7 @@ InstanceKlass::InstanceKlass(int vtable_len, set_initial_method_idnum(0); _dependencies = NULL; set_jvmti_cached_class_field_map(NULL); - set_cached_class_file(NULL, 0); + set_cached_class_file(NULL); set_initial_method_idnum(0); set_minor_version(0); set_major_version(0); @@ -1321,12 +1322,6 @@ void InstanceKlass::array_klasses_do(void f(Klass* k)) { ArrayKlass::cast(array_klasses())->array_klasses_do(f); } - -void InstanceKlass::with_array_klasses_do(void f(Klass* k)) { - f(this); - array_klasses_do(f); -} - #ifdef ASSERT static int linear_search(Array* methods, Symbol* name, Symbol* signature) { int len = methods->length(); @@ -2363,10 +2358,9 @@ void InstanceKlass::release_C_heap_structures() { } // deallocate the cached class file - if (_cached_class_file_bytes != NULL) { - os::free(_cached_class_file_bytes, mtClass); - _cached_class_file_bytes = NULL; - _cached_class_file_len = 0; + if (_cached_class_file != NULL) { + os::free(_cached_class_file, mtClass); + _cached_class_file = NULL; } // Decrement symbol reference counts associated with the unloaded class. @@ -3536,6 +3530,14 @@ Method* InstanceKlass::method_with_idnum(int idnum) { return m; } +jint InstanceKlass::get_cached_class_file_len() { + return VM_RedefineClasses::get_cached_class_file_len(_cached_class_file); +} + +unsigned char * InstanceKlass::get_cached_class_file_bytes() { + return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file); +} + // Construct a PreviousVersionNode entry for the array hung off // the InstanceKlass. diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index 6c56a519558..b82b2f83af5 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -133,6 +133,8 @@ class OopMapBlock VALUE_OBJ_CLASS_SPEC { uint _count; }; +struct JvmtiCachedClassFileData; + class InstanceKlass: public Klass { friend class VMStructs; friend class ClassFileParser; @@ -249,8 +251,8 @@ class InstanceKlass: public Klass { // InstanceKlass. See PreviousVersionWalker below. GrowableArray* _previous_versions; // JVMTI fields can be moved to their own structure - see 6315920 - unsigned char * _cached_class_file_bytes; // JVMTI: cached class file, before retransformable agent modified it in CFLH - jint _cached_class_file_len; // JVMTI: length of above + // JVMTI: cached class file, before retransformable agent modified it in CFLH + JvmtiCachedClassFileData* _cached_class_file; volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change @@ -615,11 +617,12 @@ class InstanceKlass: public Klass { static void purge_previous_versions(InstanceKlass* ik); // JVMTI: Support for caching a class file before it is modified by an agent that can do retransformation - void set_cached_class_file(unsigned char *class_file_bytes, - jint class_file_len) { _cached_class_file_len = class_file_len; - _cached_class_file_bytes = class_file_bytes; } - jint get_cached_class_file_len() { return _cached_class_file_len; } - unsigned char * get_cached_class_file_bytes() { return _cached_class_file_bytes; } + void set_cached_class_file(JvmtiCachedClassFileData *data) { + _cached_class_file = data; + } + JvmtiCachedClassFileData * get_cached_class_file() { return _cached_class_file; } + jint get_cached_class_file_len(); + unsigned char * get_cached_class_file_bytes(); // JVMTI: Support for caching of field indices, types, and offsets void set_jvmti_cached_class_field_map(JvmtiCachedClassFieldMap* descriptor) { @@ -794,7 +797,6 @@ class InstanceKlass: public Klass { void methods_do(void f(Method* method)); void array_klasses_do(void f(Klass* k)); void array_klasses_do(void f(Klass* k, TRAPS), TRAPS); - void with_array_klasses_do(void f(Klass* k)); bool super_types_do(SuperTypeClosure* blk); // Casting from Klass* @@ -874,10 +876,6 @@ class InstanceKlass: public Klass { } } - // Allocation profiling support - juint alloc_size() const { return _alloc_count * size_helper(); } - void set_alloc_size(juint n) {} - // Use this to return the size of an instance in heap words: int size_helper() const { return layout_helper_to_size_helper(layout_helper()); diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp index a719b88bbbe..41d5ec31c73 100644 --- a/hotspot/src/share/vm/oops/klass.cpp +++ b/hotspot/src/share/vm/oops/klass.cpp @@ -168,7 +168,6 @@ Klass::Klass() { set_subklass(NULL); set_next_sibling(NULL); set_next_link(NULL); - set_alloc_count(0); TRACE_INIT_ID(this); set_prototype_header(markOopDesc::prototype()); @@ -543,12 +542,6 @@ Klass* Klass::array_klass_impl(bool or_null, TRAPS) { return NULL; } - -void Klass::with_array_klasses_do(void f(Klass* k)) { - f(this); -} - - oop Klass::class_loader() const { return class_loader_data()->class_loader(); } const char* Klass::external_name() const { diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp index 5f4094d4f1e..1ca027a3762 100644 --- a/hotspot/src/share/vm/oops/klass.hpp +++ b/hotspot/src/share/vm/oops/klass.hpp @@ -79,7 +79,6 @@ // [last_biased_lock_bulk_revocation_time] (64 bits) // [prototype_header] // [biased_lock_revocation_count] -// [alloc_count ] // [_modified_oops] // [_accumulated_modified_oops] // [trace_id] @@ -171,8 +170,6 @@ class Klass : public Metadata { markOop _prototype_header; // Used when biased locking is both enabled and disabled for this type jint _biased_lock_revocation_count; - juint _alloc_count; // allocation profiling support - TRACE_DEFINE_KLASS_TRACE_ID; // Remembered sets support for the oops in the klasses. @@ -290,11 +287,6 @@ class Klass : public Metadata { void set_next_sibling(Klass* s); public: - // Allocation profiling support - juint alloc_count() const { return _alloc_count; } - void set_alloc_count(juint n) { _alloc_count = n; } - virtual juint alloc_size() const = 0; - virtual void set_alloc_size(juint n) = 0; // Compiler support static ByteSize super_offset() { return in_ByteSize(offset_of(Klass, _super)); } @@ -677,7 +669,6 @@ class Klass : public Metadata { #endif // INCLUDE_ALL_GCS virtual void array_klasses_do(void f(Klass* k)) {} - virtual void with_array_klasses_do(void f(Klass* k)); // Return self, except for abstract classes with exactly 1 // implementor. Then return the 1 concrete implementation. diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp index 5941efb3b79..ac210e14834 100644 --- a/hotspot/src/share/vm/oops/method.cpp +++ b/hotspot/src/share/vm/oops/method.cpp @@ -1163,6 +1163,7 @@ methodHandle Method::clone_with_new_data(methodHandle m, u_char* new_code, int n newm->constMethod()->set_constMethod_size(new_const_method_size); newm->set_method_size(new_method_size); assert(newm->code_size() == new_code_length, "check"); + assert(newm->method_parameters_length() == method_parameters_len, "check"); assert(newm->checked_exceptions_length() == checked_exceptions_len, "check"); assert(newm->exception_table_length() == exception_table_len, "check"); assert(newm->localvariable_table_length() == localvariable_len, "check"); @@ -1174,6 +1175,12 @@ methodHandle Method::clone_with_new_data(methodHandle m, u_char* new_code, int n new_compressed_linenumber_table, new_compressed_linenumber_size); } + // Copy method_parameters + if (method_parameters_len > 0) { + memcpy(newm->method_parameters_start(), + m->method_parameters_start(), + method_parameters_len * sizeof(MethodParametersElement)); + } // Copy checked_exceptions if (checked_exceptions_len > 0) { memcpy(newm->checked_exceptions_start(), diff --git a/hotspot/src/share/vm/opto/bytecodeInfo.cpp b/hotspot/src/share/vm/opto/bytecodeInfo.cpp index 9d2a36be28e..45ba8d758b0 100644 --- a/hotspot/src/share/vm/opto/bytecodeInfo.cpp +++ b/hotspot/src/share/vm/opto/bytecodeInfo.cpp @@ -297,15 +297,6 @@ bool InlineTree::should_not_inline(ciMethod *callee_method, } } - if (UseStringCache) { - // Do not inline StringCache::profile() method used only at the beginning. - if (callee_method->name() == ciSymbol::profile_name() && - callee_method->holder()->name() == ciSymbol::java_lang_StringCache()) { - set_msg("profiling method"); - return true; - } - } - // use frequency-based objections only for non-trivial methods if (callee_method->code_size() <= MaxTrivialSize) { return false; diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp index 58de9b62587..962c3c12894 100644 --- a/hotspot/src/share/vm/opto/matcher.cpp +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -2305,26 +2305,26 @@ void Matcher::validate_null_checks( ) { // atomic instruction acting as a store_load barrier without any // intervening volatile load, and thus we don't need a barrier here. // We retain the Node to act as a compiler ordering barrier. -bool Matcher::post_store_load_barrier(const Node *vmb) { - Compile *C = Compile::current(); - assert( vmb->is_MemBar(), "" ); - assert( vmb->Opcode() != Op_MemBarAcquire, "" ); - const MemBarNode *mem = (const MemBarNode*)vmb; +bool Matcher::post_store_load_barrier(const Node* vmb) { + Compile* C = Compile::current(); + assert(vmb->is_MemBar(), ""); + assert(vmb->Opcode() != Op_MemBarAcquire, ""); + const MemBarNode* membar = vmb->as_MemBar(); - // Get the Proj node, ctrl, that can be used to iterate forward - Node *ctrl = NULL; - DUIterator_Fast imax, i = mem->fast_outs(imax); - while( true ) { - ctrl = mem->fast_out(i); // Throw out-of-bounds if proj not found - assert( ctrl->is_Proj(), "only projections here" ); - ProjNode *proj = (ProjNode*)ctrl; - if( proj->_con == TypeFunc::Control && - !C->node_arena()->contains(ctrl) ) // Unmatched old-space only + // Get the Ideal Proj node, ctrl, that can be used to iterate forward + Node* ctrl = NULL; + for (DUIterator_Fast imax, i = membar->fast_outs(imax); i < imax; i++) { + Node* p = membar->fast_out(i); + assert(p->is_Proj(), "only projections here"); + if ((p->as_Proj()->_con == TypeFunc::Control) && + !C->node_arena()->contains(p)) { // Unmatched old-space only + ctrl = p; break; - i++; + } } + assert((ctrl != NULL), "missing control projection"); - for( DUIterator_Fast jmax, j = ctrl->fast_outs(jmax); j < jmax; j++ ) { + for (DUIterator_Fast jmax, j = ctrl->fast_outs(jmax); j < jmax; j++) { Node *x = ctrl->fast_out(j); int xop = x->Opcode(); @@ -2336,37 +2336,36 @@ bool Matcher::post_store_load_barrier(const Node *vmb) { // that a monitor exit operation contains a serializing instruction. if (xop == Op_MemBarVolatile || - xop == Op_FastLock || xop == Op_CompareAndSwapL || xop == Op_CompareAndSwapP || xop == Op_CompareAndSwapN || - xop == Op_CompareAndSwapI) + xop == Op_CompareAndSwapI) { return true; + } + + // Op_FastLock previously appeared in the Op_* list above. + // With biased locking we're no longer guaranteed that a monitor + // enter operation contains a serializing instruction. + if ((xop == Op_FastLock) && !UseBiasedLocking) { + return true; + } if (x->is_MemBar()) { // We must retain this membar if there is an upcoming volatile - // load, which will be preceded by acquire membar. - if (xop == Op_MemBarAcquire) + // load, which will be followed by acquire membar. + if (xop == Op_MemBarAcquire) { return false; - // For other kinds of barriers, check by pretending we - // are them, and seeing if we can be removed. - else - return post_store_load_barrier((const MemBarNode*)x); + } else { + // For other kinds of barriers, check by pretending we + // are them, and seeing if we can be removed. + return post_store_load_barrier(x->as_MemBar()); + } } - // Delicate code to detect case of an upcoming fastlock block - if( x->is_If() && x->req() > 1 && - !C->node_arena()->contains(x) ) { // Unmatched old-space only - Node *iff = x; - Node *bol = iff->in(1); - // The iff might be some random subclass of If or bol might be Con-Top - if (!bol->is_Bool()) return false; - assert( bol->req() > 1, "" ); - return (bol->in(1)->Opcode() == Op_FastUnlock); - } // probably not necessary to check for these - if (x->is_Call() || x->is_SafePoint() || x->is_block_proj()) + if (x->is_Call() || x->is_SafePoint() || x->is_block_proj()) { return false; + } } return false; } diff --git a/hotspot/src/share/vm/opto/parse3.cpp b/hotspot/src/share/vm/opto/parse3.cpp index acabf4985f5..28eb0580bad 100644 --- a/hotspot/src/share/vm/opto/parse3.cpp +++ b/hotspot/src/share/vm/opto/parse3.cpp @@ -294,25 +294,7 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) { // If reference is volatile, prevent following volatiles ops from // floating up before the volatile write. if (is_vol) { - // First place the specific membar for THIS volatile index. This first - // membar is dependent on the store, keeping any other membars generated - // below from floating up past the store. - int adr_idx = C->get_alias_index(adr_type); - insert_mem_bar_volatile(Op_MemBarVolatile, adr_idx, store); - - // Now place a membar for AliasIdxBot for the unknown yet-to-be-parsed - // volatile alias indices. Skip this if the membar is redundant. - if (adr_idx != Compile::AliasIdxBot) { - insert_mem_bar_volatile(Op_MemBarVolatile, Compile::AliasIdxBot, store); - } - - // Finally, place alias-index-specific membars for each volatile index - // that isn't the adr_idx membar. Typically there's only 1 or 2. - for( int i = Compile::AliasIdxRaw; i < C->num_alias_types(); i++ ) { - if (i != adr_idx && C->alias_type(i)->is_volatile()) { - insert_mem_bar_volatile(Op_MemBarVolatile, i, store); - } - } + insert_mem_bar(Op_MemBarVolatile); // Use fat membar } // If the field is final, the rules of Java say we are in or . diff --git a/hotspot/src/share/vm/prims/forte.cpp b/hotspot/src/share/vm/prims/forte.cpp index 43da7494417..4b1fbebc137 100644 --- a/hotspot/src/share/vm/prims/forte.cpp +++ b/hotspot/src/share/vm/prims/forte.cpp @@ -31,6 +31,7 @@ #include "oops/oop.inline.hpp" #include "oops/oop.inline2.hpp" #include "prims/forte.hpp" +#include "runtime/javaCalls.hpp" #include "runtime/thread.hpp" #include "runtime/vframe.hpp" #include "runtime/vframeArray.hpp" @@ -308,10 +309,14 @@ static bool find_initial_Java_frame(JavaThread* thread, for (loop_count = 0; loop_count < loop_max; loop_count++) { - if (candidate.is_first_frame()) { + if (candidate.is_entry_frame()) { + // jcw is NULL if the java call wrapper couldn't be found + JavaCallWrapper *jcw = candidate.entry_frame_call_wrapper_if_safe(thread); // If initial frame is frame from StubGenerator and there is no // previous anchor, there are no java frames associated with a method - return false; + if (jcw == NULL || jcw->is_first_frame()) { + return false; + } } if (candidate.is_interpreted_frame()) { diff --git a/hotspot/src/share/vm/prims/jni.cpp b/hotspot/src/share/vm/prims/jni.cpp index f37ea34c46d..188cf4a6e60 100644 --- a/hotspot/src/share/vm/prims/jni.cpp +++ b/hotspot/src/share/vm/prims/jni.cpp @@ -5097,7 +5097,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v // function used to determine this will always return false. Atomic::xchg // does not have this problem. if (Atomic::xchg(1, &vm_created) == 1) { - return JNI_ERR; // already created, or create attempt in progress + return JNI_EEXIST; // already created, or create attempt in progress } if (Atomic::xchg(0, &safe_to_recreate_vm) == 0) { return JNI_ERR; // someone tried and failed and retry not allowed. @@ -5138,9 +5138,21 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v event.commit(); } +#ifndef PRODUCT + #ifndef TARGET_OS_FAMILY_windows + #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) f() + #endif + // Check if we should compile all classes on bootclasspath - NOT_PRODUCT(if (CompileTheWorld) ClassLoader::compile_the_world();) - NOT_PRODUCT(if (ReplayCompiles) ciReplay::replay(thread);) + if (CompileTheWorld) ClassLoader::compile_the_world(); + if (ReplayCompiles) ciReplay::replay(thread); + + // Some platforms (like Win*) need a wrapper around these test + // functions in order to properly handle error conditions. + CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(test_error_handler); + CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(execute_internal_vm_tests); +#endif + // Since this is not a JVM_ENTRY we have to set the thread state manually before leaving. ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native); } else { @@ -5157,8 +5169,6 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v OrderAccess::release_store(&vm_created, 0); } - NOT_PRODUCT(test_error_handler(ErrorHandlerTest)); - NOT_PRODUCT(execute_internal_vm_tests()); return result; } diff --git a/hotspot/src/share/vm/prims/jniCheck.cpp b/hotspot/src/share/vm/prims/jniCheck.cpp index ad738b85ef9..c53bf392e6d 100644 --- a/hotspot/src/share/vm/prims/jniCheck.cpp +++ b/hotspot/src/share/vm/prims/jniCheck.cpp @@ -126,6 +126,7 @@ static const char * fatal_wrong_class_or_method = "Wrong object class or methodI static const char * fatal_non_weak_method = "non-weak methodID passed to JNI call"; static const char * fatal_unknown_array_object = "Unknown array object passed to JNI array operations"; static const char * fatal_object_array_expected = "Object array expected but not received for JNI array operation"; +static const char * fatal_prim_type_array_expected = "Primitive type array expected but not received for JNI array operation"; static const char * fatal_non_array = "Non-array passed to JNI array operations"; static const char * fatal_element_type_mismatch = "Array element type mismatch in JNI"; static const char * fatal_should_be_static = "Non-static field ID passed to JNI"; @@ -278,30 +279,49 @@ checkString(JavaThread* thr, jstring js) ReportJNIFatalError(thr, fatal_non_string); } -static inline void -checkArray(JavaThread* thr, jarray jArray, int elementType) +static inline arrayOop +check_is_array(JavaThread* thr, jarray jArray) { ASSERT_OOPS_ALLOWED; arrayOop aOop; aOop = (arrayOop)jniCheck::validate_object(thr, jArray); - if (aOop == NULL || !aOop->is_array()) + if (aOop == NULL || !aOop->is_array()) { ReportJNIFatalError(thr, fatal_non_array); + } + return aOop; +} - if (elementType != -1) { - if (aOop->is_typeArray()) { - BasicType array_type = TypeArrayKlass::cast(aOop->klass())->element_type(); - if (array_type != elementType) - ReportJNIFatalError(thr, fatal_element_type_mismatch); - } else if (aOop->is_objArray()) { - if ( T_OBJECT != elementType) - ReportJNIFatalError(thr, fatal_object_array_expected); - } else { - ReportJNIFatalError(thr, fatal_unknown_array_object); - } +static inline arrayOop +check_is_primitive_array(JavaThread* thr, jarray jArray) { + arrayOop aOop = check_is_array(thr, jArray); + + if (!aOop->is_typeArray()) { + ReportJNIFatalError(thr, fatal_prim_type_array_expected); + } + return aOop; +} + +static inline void +check_primitive_array_type(JavaThread* thr, jarray jArray, BasicType elementType) +{ + BasicType array_type; + arrayOop aOop; + + aOop = check_is_primitive_array(thr, jArray); + array_type = TypeArrayKlass::cast(aOop->klass())->element_type(); + if (array_type != elementType) { + ReportJNIFatalError(thr, fatal_element_type_mismatch); } } +static inline void +check_is_obj_array(JavaThread* thr, jarray jArray) { + arrayOop aOop = check_is_array(thr, jArray); + if (!aOop->is_objArray()) { + ReportJNIFatalError(thr, fatal_object_array_expected); + } +} oop jniCheck::validate_handle(JavaThread* thr, jobject obj) { if (JNIHandles::is_frame_handle(thr, obj) || @@ -1417,7 +1437,7 @@ JNI_ENTRY_CHECKED(jsize, jarray array)) functionEnter(thr); IN_VM( - checkArray(thr, array, -1); + check_is_array(thr, array); ) jsize result = UNCHECKED()->GetArrayLength(env,array); functionExit(env); @@ -1441,7 +1461,7 @@ JNI_ENTRY_CHECKED(jobject, jsize index)) functionEnter(thr); IN_VM( - checkArray(thr, array, T_OBJECT); + check_is_obj_array(thr, array); ) jobject result = UNCHECKED()->GetObjectArrayElement(env,array,index); functionExit(env); @@ -1455,7 +1475,7 @@ JNI_ENTRY_CHECKED(void, jobject val)) functionEnter(thr); IN_VM( - checkArray(thr, array, T_OBJECT); + check_is_obj_array(thr, array); ) UNCHECKED()->SetObjectArrayElement(env,array,index,val); functionExit(env); @@ -1487,7 +1507,7 @@ JNI_ENTRY_CHECKED(ElementType *, \ jboolean *isCopy)) \ functionEnter(thr); \ IN_VM( \ - checkArray(thr, array, ElementTag); \ + check_primitive_array_type(thr, array, ElementTag); \ ) \ ElementType *result = UNCHECKED()->Get##Result##ArrayElements(env, \ array, \ @@ -1513,7 +1533,7 @@ JNI_ENTRY_CHECKED(void, \ jint mode)) \ functionEnterExceptionAllowed(thr); \ IN_VM( \ - checkArray(thr, array, ElementTag); \ + check_primitive_array_type(thr, array, ElementTag); \ ASSERT_OOPS_ALLOWED; \ typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \ /* cannot check validity of copy, unless every request is logged by @@ -1543,7 +1563,7 @@ JNI_ENTRY_CHECKED(void, \ ElementType *buf)) \ functionEnter(thr); \ IN_VM( \ - checkArray(thr, array, ElementTag); \ + check_primitive_array_type(thr, array, ElementTag); \ ) \ UNCHECKED()->Get##Result##ArrayRegion(env,array,start,len,buf); \ functionExit(env); \ @@ -1567,7 +1587,7 @@ JNI_ENTRY_CHECKED(void, \ const ElementType *buf)) \ functionEnter(thr); \ IN_VM( \ - checkArray(thr, array, ElementTag); \ + check_primitive_array_type(thr, array, ElementTag); \ ) \ UNCHECKED()->Set##Result##ArrayRegion(env,array,start,len,buf); \ functionExit(env); \ @@ -1669,7 +1689,7 @@ JNI_ENTRY_CHECKED(void *, jboolean *isCopy)) functionEnterCritical(thr); IN_VM( - checkArray(thr, array, -1); + check_is_primitive_array(thr, array); ) void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy); functionExit(env); @@ -1683,7 +1703,7 @@ JNI_ENTRY_CHECKED(void, jint mode)) functionEnterCriticalExceptionAllowed(thr); IN_VM( - checkArray(thr, array, -1); + check_is_primitive_array(thr, array); ) /* The Hotspot JNI code does not use the parameters, so just check the * array parameter as a minor sanity check diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index da34f2e1316..caed2d13612 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -1121,26 +1121,6 @@ JVM_ENTRY(jobject, JVM_GetProtectionDomain(JNIEnv *env, jclass cls)) JVM_END -// Obsolete since 1.2 (Class.setProtectionDomain removed), although -// still defined in core libraries as of 1.5. -JVM_ENTRY(void, JVM_SetProtectionDomain(JNIEnv *env, jclass cls, jobject protection_domain)) - JVMWrapper("JVM_SetProtectionDomain"); - if (JNIHandles::resolve(cls) == NULL) { - THROW(vmSymbols::java_lang_NullPointerException()); - } - if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { - // Call is ignored for primitive types - Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls)); - - // cls won't be an array, as this called only from ClassLoader.defineClass - if (k->oop_is_instance()) { - oop pd = JNIHandles::resolve(protection_domain); - assert(pd == NULL || pd->is_oop(), "just checking"); - java_lang_Class::set_protection_domain(k->java_mirror(), pd); - } - } -JVM_END - static bool is_authorized(Handle context, instanceKlassHandle klass, TRAPS) { // If there is a security manager and protection domain, check the access // in the protection domain, otherwise it is authorized. diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h index 486b13531af..6248f4d793d 100644 --- a/hotspot/src/share/vm/prims/jvm.h +++ b/hotspot/src/share/vm/prims/jvm.h @@ -471,9 +471,6 @@ JVM_SetClassSigners(JNIEnv *env, jclass cls, jobjectArray signers); JNIEXPORT jobject JNICALL JVM_GetProtectionDomain(JNIEnv *env, jclass cls); -JNIEXPORT void JNICALL -JVM_SetProtectionDomain(JNIEnv *env, jclass cls, jobject protection_domain); - JNIEXPORT jboolean JNICALL JVM_IsArrayClass(JNIEnv *env, jclass cls); diff --git a/hotspot/src/share/vm/prims/jvmtiExport.cpp b/hotspot/src/share/vm/prims/jvmtiExport.cpp index f20dd4e492a..e823f376ad8 100644 --- a/hotspot/src/share/vm/prims/jvmtiExport.cpp +++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp @@ -41,6 +41,7 @@ #include "prims/jvmtiRawMonitor.hpp" #include "prims/jvmtiTagMap.hpp" #include "prims/jvmtiThreadState.inline.hpp" +#include "prims/jvmtiRedefineClasses.hpp" #include "runtime/arguments.hpp" #include "runtime/handles.hpp" #include "runtime/interfaceSupport.hpp" @@ -516,8 +517,7 @@ class JvmtiClassFileLoadHookPoster : public StackObj { jint _curr_len; unsigned char * _curr_data; JvmtiEnv * _curr_env; - jint * _cached_length_ptr; - unsigned char ** _cached_data_ptr; + JvmtiCachedClassFileData ** _cached_class_file_ptr; JvmtiThreadState * _state; KlassHandle * _h_class_being_redefined; JvmtiClassLoadKind _load_kind; @@ -526,8 +526,7 @@ class JvmtiClassFileLoadHookPoster : public StackObj { inline JvmtiClassFileLoadHookPoster(Symbol* h_name, Handle class_loader, Handle h_protection_domain, unsigned char **data_ptr, unsigned char **end_ptr, - unsigned char **cached_data_ptr, - jint *cached_length_ptr) { + JvmtiCachedClassFileData **cache_ptr) { _h_name = h_name; _class_loader = class_loader; _h_protection_domain = h_protection_domain; @@ -537,8 +536,7 @@ class JvmtiClassFileLoadHookPoster : public StackObj { _curr_len = *end_ptr - *data_ptr; _curr_data = *data_ptr; _curr_env = NULL; - _cached_length_ptr = cached_length_ptr; - _cached_data_ptr = cached_data_ptr; + _cached_class_file_ptr = cache_ptr; _state = _thread->jvmti_thread_state(); if (_state != NULL) { @@ -615,15 +613,20 @@ class JvmtiClassFileLoadHookPoster : public StackObj { } if (new_data != NULL) { // this agent has modified class data. - if (caching_needed && *_cached_data_ptr == NULL) { + if (caching_needed && *_cached_class_file_ptr == NULL) { // data has been changed by the new retransformable agent // and it hasn't already been cached, cache it - *_cached_data_ptr = (unsigned char *)os::malloc(_curr_len, mtInternal); - if (*_cached_data_ptr == NULL) { - vm_exit_out_of_memory(_curr_len, OOM_MALLOC_ERROR, "unable to allocate cached copy of original class bytes"); + JvmtiCachedClassFileData *p; + p = (JvmtiCachedClassFileData *)os::malloc( + offset_of(JvmtiCachedClassFileData, data) + _curr_len, mtInternal); + if (p == NULL) { + vm_exit_out_of_memory(offset_of(JvmtiCachedClassFileData, data) + _curr_len, + OOM_MALLOC_ERROR, + "unable to allocate cached copy of original class bytes"); } - memcpy(*_cached_data_ptr, _curr_data, _curr_len); - *_cached_length_ptr = _curr_len; + p->length = _curr_len; + memcpy(p->data, _curr_data, _curr_len); + *_cached_class_file_ptr = p; } if (_curr_data != *_data_ptr) { @@ -662,13 +665,11 @@ void JvmtiExport::post_class_file_load_hook(Symbol* h_name, Handle h_protection_domain, unsigned char **data_ptr, unsigned char **end_ptr, - unsigned char **cached_data_ptr, - jint *cached_length_ptr) { + JvmtiCachedClassFileData **cache_ptr) { JvmtiClassFileLoadHookPoster poster(h_name, class_loader, h_protection_domain, data_ptr, end_ptr, - cached_data_ptr, - cached_length_ptr); + cache_ptr); poster.post(); } diff --git a/hotspot/src/share/vm/prims/jvmtiExport.hpp b/hotspot/src/share/vm/prims/jvmtiExport.hpp index a1e0e0bd44a..dc52a32f8ea 100644 --- a/hotspot/src/share/vm/prims/jvmtiExport.hpp +++ b/hotspot/src/share/vm/prims/jvmtiExport.hpp @@ -323,8 +323,7 @@ class JvmtiExport : public AllStatic { static void post_class_file_load_hook(Symbol* h_name, Handle class_loader, Handle h_protection_domain, unsigned char **data_ptr, unsigned char **end_ptr, - unsigned char **cached_data_ptr, - jint *cached_length_ptr) NOT_JVMTI_RETURN; + JvmtiCachedClassFileData **cache_ptr) NOT_JVMTI_RETURN; static void post_native_method_bind(Method* method, address* function_ptr) NOT_JVMTI_RETURN; static void post_compiled_method_load(nmethod *nm) NOT_JVMTI_RETURN; static void post_dynamic_code_generated(const char *name, const void *code_begin, const void *code_end) NOT_JVMTI_RETURN; diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp index e9279697262..877ed0e02fb 100644 --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -3342,9 +3342,7 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass, // should get cleared in the_class too. if (the_class->get_cached_class_file_bytes() == 0) { // the_class doesn't have a cache yet so copy it - the_class->set_cached_class_file( - scratch_class->get_cached_class_file_bytes(), - scratch_class->get_cached_class_file_len()); + the_class->set_cached_class_file(scratch_class->get_cached_class_file()); } #ifndef PRODUCT else { @@ -3357,7 +3355,7 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass, // NULL out in scratch class to not delete twice. The class to be redefined // always owns these bytes. - scratch_class->set_cached_class_file(NULL, 0); + scratch_class->set_cached_class_file(NULL); // Replace inner_classes Array* old_inner_classes = the_class->inner_classes(); diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp index 3457e935bd8..97aa8143e66 100644 --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp @@ -331,6 +331,11 @@ // coordinate a cleanup of these constants with Runtime. // +struct JvmtiCachedClassFileData { + jint length; + unsigned char data[1]; +}; + class VM_RedefineClasses: public VM_Operation { private: // These static fields are needed by ClassLoaderDataGraph::classes_do() @@ -509,5 +514,12 @@ class VM_RedefineClasses: public VM_Operation { // Modifiable test must be shared between IsModifiableClass query // and redefine implementation static bool is_modifiable_class(oop klass_mirror); + + static jint get_cached_class_file_len(JvmtiCachedClassFileData *cache) { + return cache == NULL ? 0 : cache->length; + } + static unsigned char * get_cached_class_file_bytes(JvmtiCachedClassFileData *cache) { + return cache == NULL ? NULL : cache->data; + } }; #endif // SHARE_VM_PRIMS_JVMTIREDEFINECLASSES_HPP diff --git a/hotspot/src/share/vm/runtime/aprofiler.cpp b/hotspot/src/share/vm/runtime/aprofiler.cpp deleted file mode 100644 index e71bfb587ef..00000000000 --- a/hotspot/src/share/vm/runtime/aprofiler.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 1997, 2013, 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 "precompiled.hpp" -#include "classfile/systemDictionary.hpp" -#include "gc_interface/collectedHeap.inline.hpp" -#include "memory/resourceArea.hpp" -#include "memory/space.hpp" -#include "oops/oop.inline.hpp" -#include "oops/oop.inline2.hpp" -#include "runtime/aprofiler.hpp" - - -bool AllocationProfiler::_active = false; -GrowableArray* AllocationProfiler::_print_array = NULL; - - -class AllocProfClosure : public ObjectClosure { - public: - void do_object(oop obj) { - Klass* k = obj->klass(); - k->set_alloc_count(k->alloc_count() + 1); - k->set_alloc_size(k->alloc_size() + obj->size()); - } -}; - - -void AllocationProfiler::iterate_since_last_gc() { - if (is_active()) { - AllocProfClosure blk; - GenCollectedHeap* heap = GenCollectedHeap::heap(); - heap->object_iterate_since_last_GC(&blk); - } -} - - -void AllocationProfiler::engage() { - _active = true; -} - - -void AllocationProfiler::disengage() { - _active = false; -} - - -void AllocationProfiler::add_class_to_array(Klass* k) { - _print_array->append(k); -} - - -void AllocationProfiler::add_classes_to_array(Klass* k) { - // Iterate over klass and all array klasses for klass - k->with_array_klasses_do(&AllocationProfiler::add_class_to_array); -} - - -int AllocationProfiler::compare_classes(Klass** k1, Klass** k2) { - // Sort by total allocation size - return (*k2)->alloc_size() - (*k1)->alloc_size(); -} - - -int AllocationProfiler::average(size_t alloc_size, int alloc_count) { - return (int) ((double) (alloc_size * BytesPerWord) / MAX2(alloc_count, 1) + 0.5); -} - - -void AllocationProfiler::sort_and_print_array(size_t cutoff) { - _print_array->sort(&AllocationProfiler::compare_classes); - tty->print_cr("________________Size" - "__Instances" - "__Average" - "__Class________________"); - size_t total_alloc_size = 0; - int total_alloc_count = 0; - for (int index = 0; index < _print_array->length(); index++) { - Klass* k = _print_array->at(index); - size_t alloc_size = k->alloc_size(); - if (alloc_size > cutoff) { - int alloc_count = k->alloc_count(); -#ifdef PRODUCT - const char* name = k->external_name(); -#else - const char* name = k->internal_name(); -#endif - tty->print_cr("%20u %10u %8u %s", - alloc_size * BytesPerWord, - alloc_count, - average(alloc_size, alloc_count), - name); - total_alloc_size += alloc_size; - total_alloc_count += alloc_count; - } - k->set_alloc_count(0); - k->set_alloc_size(0); - } - tty->print_cr("%20u %10u %8u --total--", - total_alloc_size * BytesPerWord, - total_alloc_count, - average(total_alloc_size, total_alloc_count)); - tty->cr(); -} - - -void AllocationProfiler::print(size_t cutoff) { - ResourceMark rm; - assert(!is_active(), "AllocationProfiler cannot be active while printing profile"); - - tty->cr(); - tty->print_cr("Allocation profile (sizes in bytes, cutoff = " SIZE_FORMAT " bytes):", cutoff * BytesPerWord); - tty->cr(); - - // Print regular instance klasses and basic type array klasses - _print_array = new GrowableArray(SystemDictionary::number_of_classes()*2); - SystemDictionary::classes_do(&add_classes_to_array); - Universe::basic_type_classes_do(&add_classes_to_array); - sort_and_print_array(cutoff); - - // This used to print metadata in the permgen but since there isn't a permgen - // anymore, it is not yet implemented. -} diff --git a/hotspot/src/share/vm/runtime/aprofiler.hpp b/hotspot/src/share/vm/runtime/aprofiler.hpp deleted file mode 100644 index ba4dd7aa5c4..00000000000 --- a/hotspot/src/share/vm/runtime/aprofiler.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 1997, 2012, 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. - * - */ - -#ifndef SHARE_VM_RUNTIME_APROFILER_HPP -#define SHARE_VM_RUNTIME_APROFILER_HPP - -#include "memory/allocation.hpp" -#include "memory/universe.hpp" -#include "oops/klass.hpp" -#include "utilities/top.hpp" - -// A simple allocation profiler for Java. The profiler collects and prints -// the number and total size of instances allocated per class, including -// array classes. -// -// The profiler is currently global for all threads. It can be changed to a -// per threads profiler by keeping a more elaborate data structure and calling -// iterate_since_last_scavenge at thread switches. - - -class AllocationProfiler: AllStatic { - friend class GenCollectedHeap; - friend class G1CollectedHeap; - friend class MarkSweep; - private: - static bool _active; // tells whether profiler is active - static GrowableArray* _print_array; // temporary array for printing - - // Utility printing functions - static void add_class_to_array(Klass* k); - static void add_classes_to_array(Klass* k); - static int compare_classes(Klass** k1, Klass** k2); - static int average(size_t alloc_size, int alloc_count); - static void sort_and_print_array(size_t cutoff); - - // Call for collecting allocation information. Called at scavenge, mark-sweep and disengage. - static void iterate_since_last_gc(); - - public: - // Start profiler - static void engage(); - // Stop profiler - static void disengage(); - // Tells whether profiler is active - static bool is_active() { return _active; } - // Print profile - static void print(size_t cutoff); // Cutoff in total allocation size (in words) -}; - -#endif // SHARE_VM_RUNTIME_APROFILER_HPP diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index c52959d6976..5353d36c0e4 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -68,7 +68,6 @@ char* Arguments::_java_command = NULL; SystemProperty* Arguments::_system_properties = NULL; const char* Arguments::_gc_log_filename = NULL; bool Arguments::_has_profile = false; -bool Arguments::_has_alloc_profile = false; uintx Arguments::_min_heap_size = 0; Arguments::Mode Arguments::_mode = _mixed; bool Arguments::_java_compiler = false; @@ -261,6 +260,10 @@ static ObsoleteFlag obsolete_jvm_flags[] = { { "PrintRevisitStats", JDK_Version::jdk(8), JDK_Version::jdk(9) }, { "UseVectoredExceptions", JDK_Version::jdk(8), JDK_Version::jdk(9) }, { "UseSplitVerifier", JDK_Version::jdk(8), JDK_Version::jdk(9) }, + { "UseISM", JDK_Version::jdk(8), JDK_Version::jdk(9) }, + { "UsePermISM", JDK_Version::jdk(8), JDK_Version::jdk(9) }, + { "UseMPSS", JDK_Version::jdk(8), JDK_Version::jdk(9) }, + { "UseStringCache", JDK_Version::jdk(8), JDK_Version::jdk(9) }, #ifdef PRODUCT { "DesiredMethodLimit", JDK_Version::jdk_update(7, 2), JDK_Version::jdk(8) }, @@ -1855,8 +1858,13 @@ bool Arguments::check_gc_consistency() { "please refer to the release notes for the combinations " "allowed\n"); status = false; + } else if (ReservedCodeCacheSize > 2*G) { + // Code cache size larger than MAXINT is not supported. + jio_fprintf(defaultStream::error_stream(), + "Invalid ReservedCodeCacheSize=%dM. Must be at most %uM.\n", ReservedCodeCacheSize/M, + (2*G)/M); + status = false; } - return status; } @@ -1986,23 +1994,6 @@ bool Arguments::check_vm_args_consistency() { status = status && check_gc_consistency(); status = status && check_stack_pages(); - if (_has_alloc_profile) { - if (UseParallelGC || UseParallelOldGC) { - jio_fprintf(defaultStream::error_stream(), - "error: invalid argument combination.\n" - "Allocation profiling (-Xaprof) cannot be used together with " - "Parallel GC (-XX:+UseParallelGC or -XX:+UseParallelOldGC).\n"); - status = false; - } - if (UseConcMarkSweepGC) { - jio_fprintf(defaultStream::error_stream(), - "error: invalid argument combination.\n" - "Allocation profiling (-Xaprof) cannot be used together with " - "the CMS collector (-XX:+UseConcMarkSweepGC).\n"); - status = false; - } - } - if (CMSIncrementalMode) { if (!UseConcMarkSweepGC) { jio_fprintf(defaultStream::error_stream(), @@ -2239,8 +2230,13 @@ bool Arguments::check_vm_args_consistency() { "Invalid ReservedCodeCacheSize=%dK. Must be at least %uK.\n", ReservedCodeCacheSize/K, min_code_cache_size/K); status = false; + } else if (ReservedCodeCacheSize > 2*G) { + // Code cache size larger than MAXINT is not supported. + jio_fprintf(defaultStream::error_stream(), + "Invalid ReservedCodeCacheSize=%dM. Must be at most %uM.\n", ReservedCodeCacheSize/M, + (2*G)/M); + status = false; } - return status; } @@ -2700,9 +2696,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, "Flat profiling is not supported in this VM.\n"); return JNI_ERR; #endif // INCLUDE_FPROF - // -Xaprof - } else if (match_option(option, "-Xaprof", &tail)) { - _has_alloc_profile = true; // -Xconcurrentio } else if (match_option(option, "-Xconcurrentio", &tail)) { FLAG_SET_CMDLINE(bool, UseLWPSynchronization, true); @@ -2957,13 +2950,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, FLAG_SET_CMDLINE(bool, UseTLAB, true); } else if (match_option(option, "-XX:-UseTLE", &tail)) { FLAG_SET_CMDLINE(bool, UseTLAB, false); -SOLARIS_ONLY( - } else if (match_option(option, "-XX:+UsePermISM", &tail)) { - warning("-XX:+UsePermISM is obsolete."); - FLAG_SET_CMDLINE(bool, UseISM, true); - } else if (match_option(option, "-XX:-UsePermISM", &tail)) { - FLAG_SET_CMDLINE(bool, UseISM, false); -) } else if (match_option(option, "-XX:+DisplayVMOutputToStderr", &tail)) { FLAG_SET_CMDLINE(bool, DisplayVMOutputToStdout, false); FLAG_SET_CMDLINE(bool, DisplayVMOutputToStderr, true); @@ -3136,8 +3122,6 @@ jint Arguments::finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_req // Note that large pages are enabled/disabled for both the // Java heap and the code cache. FLAG_SET_DEFAULT(UseLargePages, false); - SOLARIS_ONLY(FLAG_SET_DEFAULT(UseMPSS, false)); - SOLARIS_ONLY(FLAG_SET_DEFAULT(UseISM, false)); } // Tiered compilation is undefined with C1. diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp index 0e84208a218..89b171f0d46 100644 --- a/hotspot/src/share/vm/runtime/arguments.hpp +++ b/hotspot/src/share/vm/runtime/arguments.hpp @@ -262,7 +262,6 @@ class Arguments : AllStatic { // Option flags static bool _has_profile; - static bool _has_alloc_profile; static const char* _gc_log_filename; static uintx _min_heap_size; @@ -464,9 +463,8 @@ class Arguments : AllStatic { // -Xloggc:, if not specified will be NULL static const char* gc_log_filename() { return _gc_log_filename; } - // -Xprof/-Xaprof + // -Xprof static bool has_profile() { return _has_profile; } - static bool has_alloc_profile() { return _has_alloc_profile; } // -Xms, -Xmx static uintx min_heap_size() { return _min_heap_size; } diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index 92af92e92eb..9ea7421cf06 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -221,9 +221,20 @@ bool frame::is_first_java_frame() const { bool frame::entry_frame_is_first() const { - return entry_frame_call_wrapper()->anchor()->last_Java_sp() == NULL; + return entry_frame_call_wrapper()->is_first_frame(); } +JavaCallWrapper* frame::entry_frame_call_wrapper_if_safe(JavaThread* thread) const { + JavaCallWrapper** jcw = entry_frame_call_wrapper_addr(); + address addr = (address) jcw; + + // addr must be within the usable part of the stack + if (thread->is_in_usable_stack(addr)) { + return *jcw; + } + + return NULL; +} bool frame::should_be_deoptimized() const { if (_deopt_state == is_deoptimized || diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp index 87581ab8da1..096b467b5f8 100644 --- a/hotspot/src/share/vm/runtime/frame.hpp +++ b/hotspot/src/share/vm/runtime/frame.hpp @@ -353,7 +353,9 @@ class frame VALUE_OBJ_CLASS_SPEC { public: // Entry frames - JavaCallWrapper* entry_frame_call_wrapper() const; + JavaCallWrapper* entry_frame_call_wrapper() const { return *entry_frame_call_wrapper_addr(); } + JavaCallWrapper* entry_frame_call_wrapper_if_safe(JavaThread* thread) const; + JavaCallWrapper** entry_frame_call_wrapper_addr() const; intptr_t* entry_frame_argument_at(int offset) const; // tells whether there is another chunk of Delta stack above diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index d07785d0b61..3a8a4493cc1 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -175,6 +175,7 @@ define_pd_global(intx, InitialCodeCacheSize, 160*K); define_pd_global(intx, ReservedCodeCacheSize, 32*M); define_pd_global(intx, CodeCacheExpansionSize, 32*K); define_pd_global(intx, CodeCacheMinBlockLength, 1); +define_pd_global(intx, CodeCacheMinimumUseSpace, 200*K); define_pd_global(uintx,MetaspaceSize, ScaleForWordSize(4*M)); define_pd_global(bool, NeverActAsServerClassMachine, true); define_pd_global(uint64_t,MaxRAM, 1ULL*G); @@ -2588,9 +2589,6 @@ class CommandLineFlags { product(bool, AggressiveOpts, false, \ "Enable aggressive optimizations - see arguments.cpp") \ \ - product(bool, UseStringCache, false, \ - "Enable String cache capabilities on String.java") \ - \ /* statistics */ \ develop(bool, CountCompiledCalls, false, \ "counts method invocations") \ @@ -3679,6 +3677,9 @@ class CommandLineFlags { develop(bool, VerifyGenericSignatures, false, \ "Abort VM on erroneous or inconsistent generic signatures") \ \ + product(bool, ParseGenericDefaults, false, \ + "Parse generic signatures for default method handling") \ + \ product(bool, UseVMInterruptibleIO, false, \ "(Unstable, Solaris-specific) Thread interrupt before or with " \ "EINTR for I/O operations results in OS_INTRPT. The default value"\ diff --git a/hotspot/src/share/vm/runtime/handles.hpp b/hotspot/src/share/vm/runtime/handles.hpp index 82506bd7049..4a2a90c74c0 100644 --- a/hotspot/src/share/vm/runtime/handles.hpp +++ b/hotspot/src/share/vm/runtime/handles.hpp @@ -227,7 +227,7 @@ class HandleArea: public Arena { HandleArea* _prev; // link to outer (older) area public: // Constructor - HandleArea(HandleArea* prev) { + HandleArea(HandleArea* prev) : Arena(Chunk::tiny_size) { debug_only(_handle_mark_nesting = 0); debug_only(_no_handle_mark_nesting = 0); _prev = prev; diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp index 7795fb92a87..ebc4e087578 100644 --- a/hotspot/src/share/vm/runtime/java.cpp +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -42,7 +42,6 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "prims/jvmtiExport.hpp" -#include "runtime/aprofiler.hpp" #include "runtime/arguments.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/compilationPolicy.hpp" @@ -509,16 +508,6 @@ void before_exit(JavaThread * thread) { } } - - if (Arguments::has_alloc_profile()) { - HandleMark hm; - // Do one last collection to enumerate all the objects - // allocated since the last one. - Universe::heap()->collect(GCCause::_allocation_profiler); - AllocationProfiler::disengage(); - AllocationProfiler::print(0); - } - if (PrintBytecodeHistogram) { BytecodeHistogram::print(); } diff --git a/hotspot/src/share/vm/runtime/javaCalls.hpp b/hotspot/src/share/vm/runtime/javaCalls.hpp index 08881fcbb88..7c397d9f4a9 100644 --- a/hotspot/src/share/vm/runtime/javaCalls.hpp +++ b/hotspot/src/share/vm/runtime/javaCalls.hpp @@ -80,6 +80,8 @@ class JavaCallWrapper: StackObj { oop receiver() { return _receiver; } void oops_do(OopClosure* f); + bool is_first_frame() const { return _anchor.last_Java_sp() == NULL; } + }; diff --git a/hotspot/src/share/vm/runtime/mutex.cpp b/hotspot/src/share/vm/runtime/mutex.cpp index 10d91fa37c4..7adc19f7a7c 100644 --- a/hotspot/src/share/vm/runtime/mutex.cpp +++ b/hotspot/src/share/vm/runtime/mutex.cpp @@ -1370,6 +1370,10 @@ void Monitor::check_prelock_state(Thread *thread) { debug_only(if (rank() != Mutex::special) \ thread->check_for_valid_safepoint_state(false);) } + if (thread->is_Watcher_thread()) { + assert(!WatcherThread::watcher_thread()->has_crash_protection(), + "locking not allowed when crash protection is set"); + } } void Monitor::check_block_state(Thread *thread) { diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp index 93a60cd06d6..a16c85785d3 100644 --- a/hotspot/src/share/vm/runtime/os.cpp +++ b/hotspot/src/share/vm/runtime/os.cpp @@ -595,6 +595,22 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) { NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1)); NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size)); +#ifdef ASSERT + // checking for the WatcherThread and crash_protection first + // since os::malloc can be called when the libjvm.{dll,so} is + // first loaded and we don't have a thread yet. + // try to find the thread after we see that the watcher thread + // exists and has crash protection. + WatcherThread *wt = WatcherThread::watcher_thread(); + if (wt != NULL && wt->has_crash_protection()) { + Thread* thread = ThreadLocalStorage::get_thread_slow(); + if (thread == wt) { + assert(!wt->has_crash_protection(), + "Can't malloc with crash protection from WatcherThread"); + } + } +#endif + if (size == 0) { // return a valid pointer if size is zero // if NULL is returned the calling functions assume out of memory. diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index e1866919df4..03497e1973b 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -32,15 +32,18 @@ #include "utilities/top.hpp" #ifdef TARGET_OS_FAMILY_linux # include "jvm_linux.h" +# include #endif #ifdef TARGET_OS_FAMILY_solaris # include "jvm_solaris.h" +# include #endif #ifdef TARGET_OS_FAMILY_windows # include "jvm_windows.h" #endif #ifdef TARGET_OS_FAMILY_bsd # include "jvm_bsd.h" +# include #endif // os defines the interface to operating system; this includes traditional @@ -507,16 +510,16 @@ class os: AllStatic { // Symbol lookup, find nearest function name; basically it implements // dladdr() for all platforms. Name of the nearest function is copied - // to buf. Distance from its base address is returned as offset. + // to buf. Distance from its base address is optionally returned as offset. // If function name is not found, buf[0] is set to '\0' and offset is - // set to -1. + // set to -1 (if offset is non-NULL). static bool dll_address_to_function_name(address addr, char* buf, int buflen, int* offset); // Locate DLL/DSO. On success, full path of the library is copied to - // buf, and offset is set to be the distance between addr and the - // library's base address. On failure, buf[0] is set to '\0' and - // offset is set to -1. + // buf, and offset is optionally set to be the distance between addr + // and the library's base address. On failure, buf[0] is set to '\0' + // and offset is set to -1 (if offset is non-NULL). static bool dll_address_to_library_name(address addr, char* buf, int buflen, int* offset); @@ -730,6 +733,10 @@ class os: AllStatic { #include "runtime/os_ext.hpp" public: + class CrashProtectionCallback : public StackObj { + public: + virtual void call() = 0; + }; // Platform dependent stuff #ifdef TARGET_OS_FAMILY_linux @@ -908,6 +915,7 @@ class os: AllStatic { char pathSep); static bool set_boot_path(char fileSep, char pathSep); static char** split_path(const char* path, int* n); + }; // Note that "PAUSE" is almost always used with synchronization @@ -915,8 +923,6 @@ class os: AllStatic { // of the global SpinPause() with C linkage. // It'd also be eligible for inlining on many platforms. -extern "C" int SpinPause () ; -extern "C" int SafeFetch32 (int * adr, int errValue) ; -extern "C" intptr_t SafeFetchN (intptr_t * adr, intptr_t errValue) ; +extern "C" int SpinPause(); #endif // SHARE_VM_RUNTIME_OS_HPP diff --git a/hotspot/src/share/vm/runtime/stubRoutines.cpp b/hotspot/src/share/vm/runtime/stubRoutines.cpp index a1179acd543..ff12ca65163 100644 --- a/hotspot/src/share/vm/runtime/stubRoutines.cpp +++ b/hotspot/src/share/vm/runtime/stubRoutines.cpp @@ -136,6 +136,13 @@ double (* StubRoutines::_intrinsic_sin )(double) = NULL; double (* StubRoutines::_intrinsic_cos )(double) = NULL; double (* StubRoutines::_intrinsic_tan )(double) = NULL; +address StubRoutines::_safefetch32_entry = NULL; +address StubRoutines::_safefetch32_fault_pc = NULL; +address StubRoutines::_safefetch32_continuation_pc = NULL; +address StubRoutines::_safefetchN_entry = NULL; +address StubRoutines::_safefetchN_fault_pc = NULL; +address StubRoutines::_safefetchN_continuation_pc = NULL; + // Initialization // // Note: to break cycle with universe initialization, stubs are generated in two phases. diff --git a/hotspot/src/share/vm/runtime/stubRoutines.hpp b/hotspot/src/share/vm/runtime/stubRoutines.hpp index b8d61ea0cbf..e43e3ab0e7b 100644 --- a/hotspot/src/share/vm/runtime/stubRoutines.hpp +++ b/hotspot/src/share/vm/runtime/stubRoutines.hpp @@ -221,6 +221,14 @@ class StubRoutines: AllStatic { static double (*_intrinsic_cos)(double); static double (*_intrinsic_tan)(double); + // Safefetch stubs. + static address _safefetch32_entry; + static address _safefetch32_fault_pc; + static address _safefetch32_continuation_pc; + static address _safefetchN_entry; + static address _safefetchN_fault_pc; + static address _safefetchN_continuation_pc; + public: // Initialization/Testing static void initialize1(); // must happen before universe::genesis @@ -381,6 +389,34 @@ class StubRoutines: AllStatic { return _intrinsic_tan(d); } + // + // Safefetch stub support + // + + typedef int (*SafeFetch32Stub)(int* adr, int errValue); + typedef intptr_t (*SafeFetchNStub) (intptr_t* adr, intptr_t errValue); + + static SafeFetch32Stub SafeFetch32_stub() { return CAST_TO_FN_PTR(SafeFetch32Stub, _safefetch32_entry); } + static SafeFetchNStub SafeFetchN_stub() { return CAST_TO_FN_PTR(SafeFetchNStub, _safefetchN_entry); } + + static bool is_safefetch_fault(address pc) { + return pc != NULL && + (pc == _safefetch32_fault_pc || + pc == _safefetchN_fault_pc); + } + + static address continuation_for_safefetch_fault(address pc) { + assert(_safefetch32_continuation_pc != NULL && + _safefetchN_continuation_pc != NULL, + "not initialized"); + + if (pc == _safefetch32_fault_pc) return _safefetch32_continuation_pc; + if (pc == _safefetchN_fault_pc) return _safefetchN_continuation_pc; + + ShouldNotReachHere(); + return NULL; + } + // // Default versions of the above arraycopy functions for platforms which do // not have specialized versions @@ -400,4 +436,15 @@ class StubRoutines: AllStatic { static void arrayof_oop_copy_uninit(HeapWord* src, HeapWord* dest, size_t count); }; +// Safefetch allows to load a value from a location that's not known +// to be valid. If the load causes a fault, the error value is returned. +inline int SafeFetch32(int* adr, int errValue) { + assert(StubRoutines::SafeFetch32_stub(), "stub not yet generated"); + return StubRoutines::SafeFetch32_stub()(adr, errValue); +} +inline intptr_t SafeFetchN(intptr_t* adr, intptr_t errValue) { + assert(StubRoutines::SafeFetchN_stub(), "stub not yet generated"); + return StubRoutines::SafeFetchN_stub()(adr, errValue); +} + #endif // SHARE_VM_RUNTIME_STUBROUTINES_HPP diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index eb2e256b1a0..bbb9a47c221 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -45,7 +45,6 @@ #include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" #include "prims/privilegedStack.hpp" -#include "runtime/aprofiler.hpp" #include "runtime/arguments.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/deoptimization.hpp" @@ -219,6 +218,7 @@ Thread::Thread() { // allocated data structures set_osthread(NULL); set_resource_area(new (mtThread)ResourceArea()); + DEBUG_ONLY(_current_resource_mark = NULL;) set_handle_area(new (mtThread) HandleArea(NULL)); set_metadata_handles(new (ResourceObj::C_HEAP, mtClass) GrowableArray(30, true)); set_active_handles(NULL); @@ -954,6 +954,14 @@ bool Thread::is_in_stack(address adr) const { } +bool Thread::is_in_usable_stack(address adr) const { + size_t stack_guard_size = os::uses_stack_guard_pages() ? (StackYellowPages + StackRedPages) * os::vm_page_size() : 0; + size_t usable_stack_size = _stack_size - stack_guard_size; + + return ((adr < stack_base()) && (adr >= stack_base() - usable_stack_size)); +} + + // We had to move these methods here, because vm threads get into ObjectSynchronizer::enter // However, there is a note in JavaThread::is_lock_owned() about the VM threads not being // used for compilation in the future. If that change is made, the need for these methods @@ -1218,7 +1226,7 @@ WatcherThread* WatcherThread::_watcher_thread = NULL; bool WatcherThread::_startable = false; volatile bool WatcherThread::_should_terminate = false; -WatcherThread::WatcherThread() : Thread() { +WatcherThread::WatcherThread() : Thread(), _crash_protection(NULL) { assert(watcher_thread() == NULL, "we can only allocate one WatcherThread"); if (os::create_thread(this, os::watcher_thread)) { _watcher_thread = this; @@ -3482,44 +3490,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { initialize_class(vmSymbols::java_lang_String(), CHECK_0); - if (AggressiveOpts) { - { - // Forcibly initialize java/util/HashMap and mutate the private - // static final "frontCacheEnabled" field before we start creating instances -#ifdef ASSERT - Klass* tmp_k = SystemDictionary::find(vmSymbols::java_util_HashMap(), Handle(), Handle(), CHECK_0); - assert(tmp_k == NULL, "java/util/HashMap should not be loaded yet"); -#endif - Klass* k_o = SystemDictionary::resolve_or_null(vmSymbols::java_util_HashMap(), Handle(), Handle(), CHECK_0); - KlassHandle k = KlassHandle(THREAD, k_o); - guarantee(k.not_null(), "Must find java/util/HashMap"); - instanceKlassHandle ik = instanceKlassHandle(THREAD, k()); - ik->initialize(CHECK_0); - fieldDescriptor fd; - // Possible we might not find this field; if so, don't break - if (ik->find_local_field(vmSymbols::frontCacheEnabled_name(), vmSymbols::bool_signature(), &fd)) { - k()->java_mirror()->bool_field_put(fd.offset(), true); - } - } - - if (UseStringCache) { - // Forcibly initialize java/lang/StringValue and mutate the private - // static final "stringCacheEnabled" field before we start creating instances - Klass* k_o = SystemDictionary::resolve_or_null(vmSymbols::java_lang_StringValue(), Handle(), Handle(), CHECK_0); - // Possible that StringValue isn't present: if so, silently don't break - if (k_o != NULL) { - KlassHandle k = KlassHandle(THREAD, k_o); - instanceKlassHandle ik = instanceKlassHandle(THREAD, k()); - ik->initialize(CHECK_0); - fieldDescriptor fd; - // Possible we might not find this field: if so, silently don't break - if (ik->find_local_field(vmSymbols::stringCacheEnabled_name(), vmSymbols::bool_signature(), &fd)) { - k()->java_mirror()->bool_field_put(fd.offset(), true); - } - } - } - } - // Initialize java_lang.System (needed before creating the thread) initialize_class(vmSymbols::java_lang_System(), CHECK_0); initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0); @@ -3637,6 +3607,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // Start Attach Listener if +StartAttachListener or it can't be started lazily if (!DisableAttachMechanism) { + AttachListener::vm_start(); if (StartAttachListener || AttachListener::init_at_startup()) { AttachListener::init(); } @@ -3677,7 +3648,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { } if (Arguments::has_profile()) FlatProfiler::engage(main_thread, true); - if (Arguments::has_alloc_profile()) AllocationProfiler::engage(); if (MemProfiling) MemProfiler::engage(); StatSampler::engage(); if (CheckJNICalls) JniPeriodicChecker::engage(); diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp index 8b8e6dd4e62..2c8c1875491 100644 --- a/hotspot/src/share/vm/runtime/thread.hpp +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -86,6 +86,8 @@ class GCTaskQueue; class ThreadClosure; class IdealGraphPrinter; +DEBUG_ONLY(class ResourceMark;) + class WorkerThread; // Class hierarchy @@ -519,6 +521,9 @@ public: // Check if address is in the stack of the thread (not just for locks). // Warning: the method can only be used on the running thread bool is_in_stack(address adr) const; + // Check if address is in the usable part of the stack (excludes protected + // guard pages) + bool is_in_usable_stack(address adr) const; // Sets this thread as starting thread. Returns failure if thread // creation fails due to lack of memory, too many threads etc. @@ -531,6 +536,8 @@ public: // Thread local resource area for temporary allocation within the VM ResourceArea* _resource_area; + DEBUG_ONLY(ResourceMark* _current_resource_mark;) + // Thread local handle area for allocation of handles within the VM HandleArea* _handle_area; GrowableArray* _metadata_handles; @@ -585,6 +592,8 @@ public: // Deadlock detection bool allow_allocation() { return _allow_allocation_count == 0; } + ResourceMark* current_resource_mark() { return _current_resource_mark; } + void set_current_resource_mark(ResourceMark* rm) { _current_resource_mark = rm; } #endif void check_for_valid_safepoint_state(bool potential_vm_operation) PRODUCT_RETURN; @@ -724,6 +733,8 @@ class WatcherThread: public Thread { static bool _startable; volatile static bool _should_terminate; // updated without holding lock + + os::WatcherThreadCrashProtection* _crash_protection; public: enum SomeConstants { delay_interval = 10 // interrupt delay in milliseconds @@ -751,6 +762,14 @@ class WatcherThread: public Thread { // Otherwise the first task to enroll will trigger the start static void make_startable(); + void set_crash_protection(os::WatcherThreadCrashProtection* crash_protection) { + assert(Thread::current()->is_Watcher_thread(), "Can only be set by WatcherThread"); + _crash_protection = crash_protection; + } + + bool has_crash_protection() const { return _crash_protection != NULL; } + os::WatcherThreadCrashProtection* crash_protection() const { return _crash_protection; } + private: int sleep() const; }; diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 94b24eec8ea..e7de9601c96 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -263,7 +263,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; unchecked_c2_static_field) \ \ /******************************************************************/ \ - /* OopDesc and Klass hierarchies (NOTE: MethodData* incomplete) */ \ + /* OopDesc and Klass hierarchies (NOTE: MethodData* incomplete) */ \ /******************************************************************/ \ \ volatile_nonstatic_field(oopDesc, _mark, markOop) \ @@ -274,21 +274,20 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; volatile_nonstatic_field(ArrayKlass, _higher_dimension, Klass*) \ volatile_nonstatic_field(ArrayKlass, _lower_dimension, Klass*) \ nonstatic_field(ArrayKlass, _vtable_len, int) \ - nonstatic_field(ArrayKlass, _alloc_size, juint) \ nonstatic_field(ArrayKlass, _component_mirror, oop) \ - nonstatic_field(CompiledICHolder, _holder_method, Method*) \ + nonstatic_field(CompiledICHolder, _holder_method, Method*) \ nonstatic_field(CompiledICHolder, _holder_klass, Klass*) \ nonstatic_field(ConstantPool, _tags, Array*) \ - nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \ + nonstatic_field(ConstantPool, _cache, ConstantPoolCache*) \ nonstatic_field(ConstantPool, _pool_holder, InstanceKlass*) \ nonstatic_field(ConstantPool, _operands, Array*) \ nonstatic_field(ConstantPool, _length, int) \ nonstatic_field(ConstantPool, _resolved_references, jobject) \ nonstatic_field(ConstantPool, _reference_map, Array*) \ nonstatic_field(ConstantPoolCache, _length, int) \ - nonstatic_field(ConstantPoolCache, _constant_pool, ConstantPool*) \ + nonstatic_field(ConstantPoolCache, _constant_pool, ConstantPool*) \ nonstatic_field(InstanceKlass, _array_klasses, Klass*) \ - nonstatic_field(InstanceKlass, _methods, Array*) \ + nonstatic_field(InstanceKlass, _methods, Array*) \ nonstatic_field(InstanceKlass, _local_interfaces, Array*) \ nonstatic_field(InstanceKlass, _transitive_interfaces, Array*) \ nonstatic_field(InstanceKlass, _fields, Array*) \ @@ -336,9 +335,8 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; nonstatic_field(Klass, _access_flags, AccessFlags) \ nonstatic_field(Klass, _subklass, Klass*) \ nonstatic_field(Klass, _next_sibling, Klass*) \ - nonstatic_field(Klass, _alloc_count, juint) \ nonstatic_field(MethodData, _size, int) \ - nonstatic_field(MethodData, _method, Method*) \ + nonstatic_field(MethodData, _method, Method*) \ nonstatic_field(MethodData, _data_size, int) \ nonstatic_field(MethodData, _data[0], intptr_t) \ nonstatic_field(MethodData, _nof_decompiles, uint) \ diff --git a/hotspot/src/share/vm/services/attachListener.hpp b/hotspot/src/share/vm/services/attachListener.hpp index 2e7cff39537..6995a0f23ab 100644 --- a/hotspot/src/share/vm/services/attachListener.hpp +++ b/hotspot/src/share/vm/services/attachListener.hpp @@ -50,6 +50,7 @@ struct AttachOperationFunctionInfo { class AttachListener: AllStatic { public: + static void vm_start() NOT_SERVICES_RETURN; static void init() NOT_SERVICES_RETURN; static void abort() NOT_SERVICES_RETURN; diff --git a/hotspot/src/share/vm/services/memBaseline.cpp b/hotspot/src/share/vm/services/memBaseline.cpp index 21eb8b5d422..62e51873c7b 100644 --- a/hotspot/src/share/vm/services/memBaseline.cpp +++ b/hotspot/src/share/vm/services/memBaseline.cpp @@ -486,7 +486,7 @@ int MemBaseline::malloc_sort_by_addr(const void* p1, const void* p2) { const MemPointerRecord* mp1 = (const MemPointerRecord*)p1; const MemPointerRecord* mp2 = (const MemPointerRecord*)p2; int delta = UNSIGNED_COMPARE(mp1->addr(), mp2->addr()); - assert(delta != 0, "dup pointer"); + assert(p1 == p2 || delta != 0, "dup pointer"); return delta; } diff --git a/hotspot/src/share/vm/services/memTracker.cpp b/hotspot/src/share/vm/services/memTracker.cpp index e0a1b29a8e9..a2961ee990a 100644 --- a/hotspot/src/share/vm/services/memTracker.cpp +++ b/hotspot/src/share/vm/services/memTracker.cpp @@ -81,13 +81,13 @@ void MemTracker::init_tracking_options(const char* option_line) { } else if (strcmp(option_line, "=detail") == 0) { // detail relies on a stack-walking ability that may not // be available depending on platform and/or compiler flags - if (PLATFORM_NMT_DETAIL_SUPPORTED) { +#if PLATFORM_NATIVE_STACK_WALKING_SUPPORTED _tracking_level = NMT_detail; - } else { +#else jio_fprintf(defaultStream::error_stream(), - "NMT detail is not supported on this platform. Using NMT summary instead."); + "NMT detail is not supported on this platform. Using NMT summary instead.\n"); _tracking_level = NMT_summary; - } +#endif } else if (strcmp(option_line, "=off") != 0) { vm_exit_during_initialization("Syntax error, expecting -XX:NativeMemoryTracking=[off|summary|detail]", NULL); } @@ -385,6 +385,7 @@ void MemTracker::enqueue_pending_recorder(MemRecorder* rec) { #define SAFE_SEQUENCE_THRESHOLD 30 #define HIGH_GENERATION_THRESHOLD 60 #define MAX_RECORDER_THREAD_RATIO 30 +#define MAX_RECORDER_PER_THREAD 100 void MemTracker::sync() { assert(_tracking_level > NMT_off, "NMT is not enabled"); @@ -437,6 +438,11 @@ void MemTracker::sync() { // means that worker thread is lagging behind in processing them. if (!AutoShutdownNMT) { _slowdown_calling_thread = (MemRecorder::_instance_count > MAX_RECORDER_THREAD_RATIO * _thread_count); + } else { + // If auto shutdown is on, enforce MAX_RECORDER_PER_THREAD threshold to prevent OOM + if (MemRecorder::_instance_count >= _thread_count * MAX_RECORDER_PER_THREAD) { + shutdown(NMT_out_of_memory); + } } // check _worker_thread with lock to avoid racing condition diff --git a/hotspot/src/share/vm/services/memTracker.hpp b/hotspot/src/share/vm/services/memTracker.hpp index dc7b78859d3..364c6b4f235 100644 --- a/hotspot/src/share/vm/services/memTracker.hpp +++ b/hotspot/src/share/vm/services/memTracker.hpp @@ -470,7 +470,21 @@ class MemTracker : AllStatic { static void check_NMT_load(Thread* thr) { assert(thr != NULL, "Sanity check"); if (_slowdown_calling_thread && thr != _worker_thread) { +#ifdef _WINDOWS + // On Windows, os::NakedYield() does not work as well + // as os::yield_all() os::yield_all(); +#else + // On Solaris, os::yield_all() depends on os::sleep() + // which requires JavaTherad in _thread_in_vm state. + // Transits thread to _thread_in_vm state can be dangerous + // if caller holds lock, as it may deadlock with Threads_lock. + // So use NaKedYield instead. + // + // Linux and BSD, NakedYield() and yield_all() implementations + // are the same. + os::NakedYield(); +#endif } } diff --git a/hotspot/src/share/vm/trace/traceDataTypes.hpp b/hotspot/src/share/vm/trace/traceDataTypes.hpp index 437ac447e73..31004d934b5 100644 --- a/hotspot/src/share/vm/trace/traceDataTypes.hpp +++ b/hotspot/src/share/vm/trace/traceDataTypes.hpp @@ -63,5 +63,7 @@ typedef u8 stacktraceid; typedef u8 methodid; typedef u8 fieldid; +class TraceUnicodeString; + #endif // SHARE_VM_TRACE_TRACEDATATYPES_HPP diff --git a/hotspot/src/share/vm/trace/tracetypes.xml b/hotspot/src/share/vm/trace/tracetypes.xml index 7f0460e691a..22fd5059042 100644 --- a/hotspot/src/share/vm/trace/tracetypes.xml +++ b/hotspot/src/share/vm/trace/tracetypes.xml @@ -55,18 +55,6 @@ Before we can use it we need also define a primary field data type: type="u8" sizeop="sizeof(u1)"/> Now we can use the content + data type in declaring event fields. -Remember however, that for us to be able to resolve the value later we must also add -creating the constant pool data in VM_JFRCheckpoint::write_checkpoint - - ... - //CGMODE - w->be_uint(CONTENT_TYPE_GCMODE); - w->be_uint(MM_GC_MODE_UNINITIALIZED); - for (i = 0; i < MM_GC_MODE_UNINITIALIZED; i++) { - w->uchar(i); - w->write_utf8(gcModeGetName(i)); - } - --> @@ -81,10 +69,6 @@ creating the constant pool data in VM_JFRCheckpoint::write_checkpoint - @@ -285,6 +269,10 @@ creating the constant pool data in VM_JFRCheckpoint::write_checkpoint + + + - - @@ -235,44 +238,31 @@ - + - - - - - - - +grant codeBase "file:/${basedir}/${nashorn.internal.tests.jar}" { + permission java.security.AllPermission; +}; - - - - - - - +grant codeBase "file:/${basedir}/${file.reference.testng.jar}" { + permission java.security.AllPermission; +}; - - - - - - - +grant codeBase "file:/${basedir}/test/script/trusted/*" { + permission java.security.AllPermission; +}; - - - - - - - - - - - - +grant codeBase "file:/${basedir}/test/script/basic/*" { + permission java.io.FilePermission "${basedir}/test/script/-", "read"; + permission java.io.FilePermission "$${user.dir}", "read"; + permission java.util.PropertyPermission "user.dir", "read"; + permission java.util.PropertyPermission "nashorn.test.*", "read"; +}; + +grant codeBase "file:/${basedir}/test/script/basic/JDK-8010946-privileged.js" { + permission java.util.PropertyPermission "java.security.policy", "read"; +}; + \/ /// diff --git a/nashorn/make/code_coverage.xml b/nashorn/make/code_coverage.xml index 3e2860f8d9d..dea03099e36 100644 --- a/nashorn/make/code_coverage.xml +++ b/nashorn/make/code_coverage.xml @@ -60,16 +60,8 @@ - - - - - - - - diff --git a/nashorn/make/project.properties b/nashorn/make/project.properties index 66bbdbebab7..dbd9efce80c 100644 --- a/nashorn/make/project.properties +++ b/nashorn/make/project.properties @@ -200,6 +200,9 @@ test262-test-sys-prop.test.failed.list.file=${build.dir}/test/failedTests # test262 test frameworks test262-test-sys-prop.test.js.framework=\ + --class-cache-size=0 \ + --no-java \ + --no-typed-arrays \ -timezone=PST \ ${test.script.dir}/test262.js \ ${test262.dir}/test/harness/framework.js \ diff --git a/nashorn/makefiles/BuildNashorn.gmk b/nashorn/makefiles/BuildNashorn.gmk index d08f7e8fa6f..96bedf44ac4 100644 --- a/nashorn/makefiles/BuildNashorn.gmk +++ b/nashorn/makefiles/BuildNashorn.gmk @@ -71,7 +71,6 @@ $(eval $(call SetupJavaCompilation,BUILD_NASGEN,\ $(BUILD_NASGEN): $(BUILD_NASHORN) # Copy classes to final classes dir and run nasgen to modify classes in jdk.nashorn.internal.objects package -# Finally rename classes in jdk.nashorn.internal.objects package $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run: $(BUILD_NASGEN) $(ECHO) Running nasgen $(MKDIR) -p $(@D) @@ -80,9 +79,6 @@ $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run: $(BUILD_NASGEN) $(FIXPATH) $(JAVA) \ -cp "$(NASHORN_OUTPUTDIR)/nasgen_classes$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes" \ jdk.nashorn.internal.tools.nasgen.Main $(@D) jdk.nashorn.internal.objects $(@D) - for f in `$(FIND) $(@D)/jdk/nashorn/internal/objects/ -name "*.class"`; do \ - mv "$$f" `$(ECHO) "$$f" | $(SED) "s/\.class$$/\.clazz/"`; \ - done $(TOUCH) $@ # Version file needs to be processed with version numbers @@ -104,7 +100,7 @@ $(eval $(call SetupArchive,BUILD_NASHORN_JAR,\ $(NASHORN_OUTPUTDIR)/classes/_the.nasgen.run \ $(VERSION_FILE),\ SRCS:=$(NASHORN_OUTPUTDIR)/classes,\ - SUFFIXES:=.class .clazz .js .properties Factory,\ + SUFFIXES:=.class .js .properties Factory,\ MANIFEST:=$(NASHORN_TOPDIR)/src/META-INF/MANIFEST.MF,\ EXTRA_MANIFEST_ATTR:=$(MANIFEST_ATTRIBUTES),\ SKIP_METAINF:=true,\ diff --git a/nashorn/src/jdk/internal/dynalink/DynamicLinker.java b/nashorn/src/jdk/internal/dynalink/DynamicLinker.java index c0930dae790..155ff309d01 100644 --- a/nashorn/src/jdk/internal/dynalink/DynamicLinker.java +++ b/nashorn/src/jdk/internal/dynalink/DynamicLinker.java @@ -144,6 +144,9 @@ public class DynamicLinker { private static final String CLASS_NAME = DynamicLinker.class.getName(); private static final String RELINK_METHOD_NAME = "relink"; + private static final String INITIAL_LINK_CLASS_NAME = "java.lang.invoke.MethodHandleNatives"; + private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite"; + private final LinkerServices linkerServices; private final int runtimeContextArgCount; private final boolean syncOnRelink; @@ -262,20 +265,54 @@ public class DynamicLinker { } /** - * Returns a stack trace element describing the location of the call site currently being relinked on the current + * Returns a stack trace element describing the location of the call site currently being linked on the current * thread. The operation internally creates a Throwable object and inspects its stack trace, so it's potentially * expensive. The recommended usage for it is in writing diagnostics code. - * @return a stack trace element describing the location of the call site currently being relinked, or null if it is - * not invoked while a call site is being relinked. + * @return a stack trace element describing the location of the call site currently being linked, or null if it is + * not invoked while a call site is being linked. */ - public static StackTraceElement getRelinkedCallSiteLocation() { + public static StackTraceElement getLinkedCallSiteLocation() { final StackTraceElement[] trace = new Throwable().getStackTrace(); for(int i = 0; i < trace.length - 1; ++i) { final StackTraceElement frame = trace[i]; - if(RELINK_METHOD_NAME.equals(frame.getMethodName()) && CLASS_NAME.equals(frame.getClassName())) { + if(isRelinkFrame(frame) || isInitialLinkFrame(frame)) { return trace[i + 1]; } } return null; } + + /** + * Deprecated because of not precise name. + * @deprecated Use {@link #getLinkedCallSiteLocation()} instead. + * @return see non-deprecated method + */ + @Deprecated + public static StackTraceElement getRelinkedCallSiteLocation() { + return getLinkedCallSiteLocation(); + } + + /** + * Returns true if the frame represents {@code MethodHandleNatives.linkCallSite()}, the frame immediately on top of + * the call site frame when the call site is being linked for the first time. + * @param frame the frame + * @return true if this frame represents {@code MethodHandleNatives.linkCallSite()} + */ + private static boolean isInitialLinkFrame(final StackTraceElement frame) { + return testFrame(frame, INITIAL_LINK_METHOD_NAME, INITIAL_LINK_CLASS_NAME); + } + + /** + * Returns true if the frame represents {@code DynamicLinker.relink()}, the frame immediately on top of the call + * site frame when the call site is being relinked (linked for second and subsequent times). + * @param frame the frame + * @return true if this frame represents {@code DynamicLinker.relink()} + */ + private static boolean isRelinkFrame(final StackTraceElement frame) { + return testFrame(frame, RELINK_METHOD_NAME, CLASS_NAME); + } + + private static boolean testFrame(final StackTraceElement frame, final String methodName, final String className) { + return methodName.equals(frame.getMethodName()) && className.equals(frame.getClassName()); + } } diff --git a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java index 702c1509fd4..0c8d3efde7f 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java +++ b/nashorn/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java @@ -86,9 +86,14 @@ package jdk.internal.dynalink.beans; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -109,10 +114,11 @@ import jdk.internal.dynalink.support.Lookup; * @author Attila Szegedi */ abstract class AbstractJavaLinker implements GuardingDynamicLinker { + final Class clazz; private final MethodHandle classGuard; private final MethodHandle assignableGuard; - private final Map propertyGetters = new HashMap<>(); + private final Map propertyGetters = new HashMap<>(); private final Map propertySetters = new HashMap<>(); private final Map methods = new HashMap<>(); @@ -129,22 +135,19 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { // Add methods and properties for(Method method: introspector.getMethods()) { final String name = method.getName(); - final MethodHandle methodHandle = introspector.unreflect(method); // Add method - addMember(name, methodHandle, methods); + addMember(name, method, methods); // Add the method as a property getter and/or setter if(name.startsWith("get") && name.length() > 3 && method.getParameterTypes().length == 0) { // Property getter - setPropertyGetter(decapitalize(name.substring(3)), introspector.unreflect( - getMostGenericGetter(method)), ValidationType.INSTANCE_OF); + setPropertyGetter(method, 3); } else if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0 && method.getReturnType() == boolean.class) { // Boolean property getter - setPropertyGetter(decapitalize(name.substring(2)), introspector.unreflect( - getMostGenericGetter(method)), ValidationType.INSTANCE_OF); + setPropertyGetter(method, 2); } else if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1) { // Property setter - addMember(decapitalize(name.substring(3)), methodHandle, propertySetters); + addMember(decapitalize(name.substring(3)), method, propertySetters); } } @@ -156,7 +159,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { setPropertyGetter(name, introspector.unreflectGetter(field), ValidationType.EXACT_CLASS); } if(!(Modifier.isFinal(field.getModifiers()) || propertySetters.containsKey(name))) { - addMember(name, introspector.unreflectSetter(field), propertySetters); + addMember(name, new SimpleDynamicMethod(introspector.unreflectSetter(field), clazz, name), + propertySetters); } } @@ -192,38 +196,135 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { abstract FacetIntrospector createFacetIntrospector(); - void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) { - propertyGetters.put(name, new AnnotatedMethodHandle(handle, validationType)); + Collection getReadablePropertyNames() { + return getUnmodifiableKeys(propertyGetters); } - private void addMember(String name, MethodHandle mh, Map methodMap) { + Collection getWritablePropertyNames() { + return getUnmodifiableKeys(propertySetters); + } + + Collection getMethodNames() { + return getUnmodifiableKeys(methods); + } + + private static Collection getUnmodifiableKeys(Map m) { + return Collections.unmodifiableCollection(m.keySet()); + } + + /** + * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only + * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties + * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)} + * instead. + * @param name name of the property + * @param handle the method handle that implements the property getter + * @param validationType the validation type for the property + */ + private void setPropertyGetter(String name, SingleDynamicMethod handle, ValidationType validationType) { + propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType)); + } + + /** + * Sets the specified reflective method to be the property getter for the specified property. + * @param getter the getter method + * @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for + * names starting with "is". + */ + private void setPropertyGetter(Method getter, int prefixLen) { + setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod( + getMostGenericGetter(getter)), ValidationType.INSTANCE_OF); + } + + /** + * Sets the specified method handle to be the property getter for the specified property. Note that you can only + * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties + * that are caller-sensitive, you must use {@link #setPropertyGetter(String, SingleDynamicMethod, ValidationType)} + * instead. + * @param name name of the property + * @param handle the method handle that implements the property getter + * @param validationType the validation type for the property + */ + void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) { + setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType); + } + + private void addMember(String name, AccessibleObject ao, Map methodMap) { + addMember(name, createDynamicMethod(ao), methodMap); + } + + private void addMember(String name, SingleDynamicMethod method, Map methodMap) { final DynamicMethod existingMethod = methodMap.get(name); - final DynamicMethod newMethod = addMember(mh, existingMethod, clazz, name); + final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name); if(newMethod != existingMethod) { methodMap.put(name, newMethod); } } - static DynamicMethod createDynamicMethod(Iterable methodHandles, Class clazz, String name) { + /** + * Given one or more reflective methods or constructors, creates a dynamic method that represents them all. The + * methods should represent all overloads of the same name (or all constructors of the class). + * @param members the reflective members + * @param clazz the class declaring the reflective members + * @param name the common name of the reflective members. + * @return a dynamic method representing all the specified reflective members. + */ + static DynamicMethod createDynamicMethod(Iterable members, Class clazz, String name) { DynamicMethod dynMethod = null; - for(MethodHandle methodHandle: methodHandles) { - dynMethod = addMember(methodHandle, dynMethod, clazz, name); + for(AccessibleObject method: members) { + dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name); } return dynMethod; } - private static DynamicMethod addMember(MethodHandle mh, DynamicMethod existing, Class clazz, String name) { + /** + * Given a reflective method or a constructor, creates a dynamic method that represents it. This method will + * distinguish between caller sensitive and ordinary methods/constructors, and create appropriate caller sensitive + * dynamic method when needed. + * @param m the reflective member + * @return the single dynamic method representing the reflective member + */ + private static SingleDynamicMethod createDynamicMethod(AccessibleObject m) { + if(CallerSensitiveDetector.isCallerSensitive(m)) { + return new CallerSensitiveDynamicMethod(m); + } + final Member member = (Member)m; + return new SimpleDynamicMethod(unreflectSafely(m), member.getDeclaringClass(), member.getName()); + } + + /** + * Unreflects a method handle from a Method or a Constructor using safe (zero-privilege) unreflection. Should be + * only used for methods and constructors that are not caller sensitive. If a caller sensitive method were + * unreflected through this mechanism, it would not be a security issue, but would be bound to the zero-privilege + * unreflector as its caller, and thus completely useless. + * @param m the method or constructor + * @return the method handle + */ + private static MethodHandle unreflectSafely(AccessibleObject m) { + if(m instanceof Method) { + final Method reflMethod = (Method)m; + final MethodHandle handle = SafeUnreflector.unreflect(reflMethod); + if(Modifier.isStatic(reflMethod.getModifiers())) { + return StaticClassIntrospector.editStaticMethodHandle(handle); + } + return handle; + } + return StaticClassIntrospector.editConstructorMethodHandle(SafeUnreflector.unreflectConstructor( + (Constructor)m)); + } + + private static DynamicMethod mergeMethods(SingleDynamicMethod method, DynamicMethod existing, Class clazz, String name) { if(existing == null) { - return new SimpleDynamicMethod(mh, clazz, name); - } else if(existing.contains(mh)) { + return method; + } else if(existing.contains(method)) { return existing; - } else if(existing instanceof SimpleDynamicMethod) { + } else if(existing instanceof SingleDynamicMethod) { final OverloadedDynamicMethod odm = new OverloadedDynamicMethod(clazz, name); - odm.addMethod(((SimpleDynamicMethod)existing)); - odm.addMethod(mh); + odm.addMethod(((SingleDynamicMethod)existing)); + odm.addMethod(method); return odm; } else if(existing instanceof OverloadedDynamicMethod) { - ((OverloadedDynamicMethod)existing).addMethod(mh); + ((OverloadedDynamicMethod)existing).addMethod(method); return existing; } throw new AssertionError(); @@ -296,7 +397,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { private GuardedInvocation getCallPropWithThis(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) { switch(callSiteDescriptor.getNameTokenCount()) { case 3: { - return createGuardedDynamicMethodInvocation(callSiteDescriptor.getMethodType(), linkerServices, + return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), methods); } default: { @@ -305,16 +406,16 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { } } - private GuardedInvocation createGuardedDynamicMethodInvocation(MethodType callSiteType, + private GuardedInvocation createGuardedDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices, String methodName, Map methodMap){ - final MethodHandle inv = getDynamicMethodInvocation(callSiteType, linkerServices, methodName, methodMap); - return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteType)); + final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap); + return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType())); } - private static MethodHandle getDynamicMethodInvocation(MethodType callSiteType, LinkerServices linkerServices, - String methodName, Map methodMap) { + private static MethodHandle getDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor, + LinkerServices linkerServices, String methodName, Map methodMap) { final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap); - return dynaMethod != null ? dynaMethod.getInvocation(callSiteType, linkerServices) : null; + return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null; } private static DynamicMethod getDynamicMethod(String methodName, Map methodMap) { @@ -322,13 +423,13 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap); } - private static SimpleDynamicMethod getExplicitSignatureDynamicMethod(String methodName, + private static SingleDynamicMethod getExplicitSignatureDynamicMethod(String methodName, Map methodsMap) { // What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name // to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method // resolution works correctly in almost every situation. However, in presence of many language-specific // conversions with a radically dynamic language, most overloaded methods will end up being constantly selected - // at invocation time, so a programmer knowledgable of the situation might choose to pin down an exact overload + // at invocation time, so a programmer knowledgeable of the situation might choose to pin down an exact overload // for performance reasons. // Is the method name lexically of the form "name(types)"? @@ -377,8 +478,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { final MethodType setterType = type.dropParameterTypes(1, 2); // Bind property setter handle to the expected setter type and linker services. Type is // MethodHandle(Object, String, Object) - final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, setterType, - linkerServices); + final MethodHandle boundGetter = MethodHandles.insertArguments(getPropertySetterHandle, 0, + CallSiteDescriptorFactory.dropParameterTypes(callSiteDescriptor, 1, 2), linkerServices); // Cast getter to MethodHandle(O, N, V) final MethodHandle typedGetter = linkerServices.asType(boundGetter, type.changeReturnType( @@ -415,9 +516,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { case 3: { // Must have two arguments: target object and property value assertParameterCount(callSiteDescriptor, 2); - final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor.getMethodType(), - linkerServices, callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), - propertySetters); + final GuardedInvocation gi = createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices, + callSiteDescriptor.getNameToken(CallSiteDescriptor.NAME_OPERAND), propertySetters); // If we have a property setter with this name, this composite operation will always stop here if(gi != null) { return new GuardedInvocationComponent(gi, clazz, ValidationType.EXACT_CLASS); @@ -435,14 +535,13 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { private static final Lookup privateLookup = new Lookup(MethodHandles.lookup()); - private static final MethodHandle IS_ANNOTATED_HANDLE_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( - boolean.class, AnnotatedMethodHandle.class)); - private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_HANDLE = MethodHandles.dropArguments( - MethodHandles.constant(Object.class, null), 0, AnnotatedMethodHandle.class); - private static final MethodHandle GET_ANNOTATED_HANDLE = privateLookup.findGetter(AnnotatedMethodHandle.class, - "handle", MethodHandle.class); - private static final MethodHandle GENERIC_PROPERTY_GETTER_HANDLER_INVOKER = MethodHandles.filterArguments( - MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)), 0, GET_ANNOTATED_HANDLE); + private static final MethodHandle IS_ANNOTATED_METHOD_NOT_NULL = Guards.isNotNull().asType(MethodType.methodType( + boolean.class, AnnotatedDynamicMethod.class)); + private static final MethodHandle CONSTANT_NULL_DROP_ANNOTATED_METHOD = MethodHandles.dropArguments( + MethodHandles.constant(Object.class, null), 0, AnnotatedDynamicMethod.class); + private static final MethodHandle GET_ANNOTATED_METHOD = privateLookup.findVirtual(AnnotatedDynamicMethod.class, + "getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class)); + private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class)); private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices, List ops) throws Exception { @@ -455,16 +554,20 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { // What's below is basically: // foldArguments(guardWithTest(isNotNull, invoke(get_handle), null|nextComponent.invocation), get_getter_handle) // only with a bunch of method signature adjustments. Basically, retrieve method getter - // AnnotatedMethodHandle; if it is non-null, invoke its "handle" field, otherwise either return null, + // AnnotatedDynamicMethod; if it is non-null, invoke its "handle" field, otherwise either return null, // or delegate to next component's invocation. final MethodHandle typedGetter = linkerServices.asType(getPropertyGetterHandle, type.changeReturnType( - AnnotatedMethodHandle.class)); - // Object(AnnotatedMethodHandle, Object)->R(AnnotatedMethodHandle, T0) - final MethodHandle invokeHandleTyped = linkerServices.asType(GENERIC_PROPERTY_GETTER_HANDLER_INVOKER, - MethodType.methodType(type.returnType(), AnnotatedMethodHandle.class, type.parameterType(0))); + AnnotatedDynamicMethod.class)); + final MethodHandle callSiteBoundMethodGetter = MethodHandles.insertArguments( + GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup()); + final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0, + callSiteBoundMethodGetter); + // Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0) + final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker, + MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0))); // Since it's in the target of a fold, drop the unnecessary second argument - // R(AnnotatedMethodHandle, T0)->R(AnnotatedMethodHandle, T0, T1) + // R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1) final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2, type.parameterType(1)); final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor, @@ -472,19 +575,19 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { final MethodHandle fallbackFolded; if(nextComponent == null) { - // Object(AnnotatedMethodHandle)->R(AnnotatedMethodHandle, T0, T1); returns constant null - fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_HANDLE, 1, - type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedMethodHandle.class)); + // Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null + fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1, + type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class)); } else { - // R(T0, T1)->R(AnnotatedMethodHAndle, T0, T1); adapts the next component's invocation to drop the + // R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the // extra argument resulting from fold fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(), - 0, AnnotatedMethodHandle.class); + 0, AnnotatedDynamicMethod.class); } - // fold(R(AnnotatedMethodHandle, T0, T1), AnnotatedMethodHandle(T0, T1)) + // fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1)) final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest( - IS_ANNOTATED_HANDLE_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); + IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter); if(nextComponent == null) { return getClassGuardedInvocationComponent(compositeGetter, type); } @@ -494,13 +597,13 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { // Must have exactly one argument: receiver assertParameterCount(callSiteDescriptor, 1); // Fixed name - final AnnotatedMethodHandle annGetter = propertyGetters.get(callSiteDescriptor.getNameToken( + final AnnotatedDynamicMethod annGetter = propertyGetters.get(callSiteDescriptor.getNameToken( CallSiteDescriptor.NAME_OPERAND)); if(annGetter == null) { // We have no such property, always delegate to the next component operation return getGuardedInvocationComponent(callSiteDescriptor, linkerServices, ops); } - final MethodHandle getter = annGetter.handle; + final MethodHandle getter = annGetter.getInvocation(callSiteDescriptor, linkerServices); // NOTE: since property getters (not field getters!) are no-arg, we don't have to worry about them being // overloaded in a subclass. Therefore, we can discover the most abstract superclass that has the // method, and use that as the guard with Guards.isInstance() for a more stably linked call site. If @@ -508,6 +611,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { // NOTE: No delegation to the next component operation if we have a property with this name, even if its // value is null. final ValidationType validationType = annGetter.validationType; + // TODO: we aren't using the type that declares the most generic getter here! return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType, type), clazz, validationType); } @@ -623,14 +727,15 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { // args are dropped; this makes handles with first three args conform to "Object, String, Object" though, which is // a typical property setter with variable name signature (target, name, value). private static final MethodHandle GET_PROPERTY_SETTER_HANDLE = MethodHandles.dropArguments(MethodHandles.dropArguments( - privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, MethodType.class, + privateLookup.findOwnSpecial("getPropertySetterHandle", MethodHandle.class, CallSiteDescriptor.class, LinkerServices.class, Object.class), 3, Object.class), 5, Object.class); // Type is MethodHandle(MethodType, LinkerServices, Object, String, Object) private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this); @SuppressWarnings("unused") - private MethodHandle getPropertySetterHandle(MethodType setterType, LinkerServices linkerServices, Object id) { - return getDynamicMethodInvocation(setterType, linkerServices, String.valueOf(id), propertySetters); + private MethodHandle getPropertySetterHandle(CallSiteDescriptor setterDescriptor, LinkerServices linkerServices, + Object id) { + return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters); } private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial( @@ -689,13 +794,24 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker { return null; } - private static final class AnnotatedMethodHandle { - final MethodHandle handle; + private static final class AnnotatedDynamicMethod { + private final SingleDynamicMethod method; /*private*/ final ValidationType validationType; - AnnotatedMethodHandle(MethodHandle handle, ValidationType validationType) { - this.handle = handle; + AnnotatedDynamicMethod(SingleDynamicMethod method, ValidationType validationType) { + this.method = method; this.validationType = validationType; } + + MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) { + return method.getInvocation(callSiteDescriptor, linkerServices); + } + + @SuppressWarnings("unused") + MethodHandle getTarget(MethodHandles.Lookup lookup) { + MethodHandle inv = method.getTarget(lookup); + assert inv != null; + return inv; + } } } diff --git a/nashorn/src/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java b/nashorn/src/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java index a232caf137a..39a03a8ef96 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java +++ b/nashorn/src/jdk/internal/dynalink/beans/ApplicableOverloadedMethods.java @@ -83,7 +83,6 @@ package jdk.internal.dynalink.beans; -import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.LinkedList; import java.util.List; @@ -95,7 +94,7 @@ import jdk.internal.dynalink.support.TypeUtilities; * @author Attila Szegedi */ class ApplicableOverloadedMethods { - private final List methods; + private final List methods; private final boolean varArgs; /** @@ -106,10 +105,10 @@ class ApplicableOverloadedMethods { * @param test applicability test. One of {@link #APPLICABLE_BY_SUBTYPING}, * {@link #APPLICABLE_BY_METHOD_INVOCATION_CONVERSION}, or {@link #APPLICABLE_BY_VARIABLE_ARITY}. */ - ApplicableOverloadedMethods(final List methods, final MethodType callSiteType, + ApplicableOverloadedMethods(final List methods, final MethodType callSiteType, final ApplicabilityTest test) { this.methods = new LinkedList<>(); - for(MethodHandle m: methods) { + for(SingleDynamicMethod m: methods) { if(test.isApplicable(callSiteType, m)) { this.methods.add(m); } @@ -122,7 +121,7 @@ class ApplicableOverloadedMethods { * * @return list of all methods. */ - List getMethods() { + List getMethods() { return methods; } @@ -131,12 +130,12 @@ class ApplicableOverloadedMethods { * * @return a list of maximally specific methods. */ - List findMaximallySpecificMethods() { + List findMaximallySpecificMethods() { return MaximallySpecific.getMaximallySpecificMethods(methods, varArgs); } abstract static class ApplicabilityTest { - abstract boolean isApplicable(MethodType callSiteType, MethodHandle method); + abstract boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method); } /** @@ -144,8 +143,8 @@ class ApplicableOverloadedMethods { */ static final ApplicabilityTest APPLICABLE_BY_SUBTYPING = new ApplicabilityTest() { @Override - boolean isApplicable(MethodType callSiteType, MethodHandle method) { - final MethodType methodType = method.type(); + boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) { + final MethodType methodType = method.getMethodType(); final int methodArity = methodType.parameterCount(); if(methodArity != callSiteType.parameterCount()) { return false; @@ -166,8 +165,8 @@ class ApplicableOverloadedMethods { */ static final ApplicabilityTest APPLICABLE_BY_METHOD_INVOCATION_CONVERSION = new ApplicabilityTest() { @Override - boolean isApplicable(MethodType callSiteType, MethodHandle method) { - final MethodType methodType = method.type(); + boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) { + final MethodType methodType = method.getMethodType(); final int methodArity = methodType.parameterCount(); if(methodArity != callSiteType.parameterCount()) { return false; @@ -189,11 +188,11 @@ class ApplicableOverloadedMethods { */ static final ApplicabilityTest APPLICABLE_BY_VARIABLE_ARITY = new ApplicabilityTest() { @Override - boolean isApplicable(MethodType callSiteType, MethodHandle method) { - if(!method.isVarargsCollector()) { + boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) { + if(!method.isVarArgs()) { return false; } - final MethodType methodType = method.type(); + final MethodType methodType = method.getMethodType(); final int methodArity = methodType.parameterCount(); final int fixArity = methodArity - 1; final int callSiteArity = callSiteType.parameterCount(); diff --git a/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java b/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java index 85811235699..e0c14b0d1d8 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java +++ b/nashorn/src/jdk/internal/dynalink/beans/BeansLinker.java @@ -84,6 +84,8 @@ package jdk.internal.dynalink.beans; import java.lang.invoke.MethodHandles; +import java.util.Collection; +import java.util.Collections; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.DynamicLinkerFactory; import jdk.internal.dynalink.linker.GuardedInvocation; @@ -166,6 +168,72 @@ public class BeansLinker implements GuardingDynamicLinker { return obj instanceof DynamicMethod; } + /** + * Returns a collection of names of all readable instance properties of a class. + * @param clazz the class + * @return a collection of names of all readable instance properties of a class. + */ + public static Collection getReadableInstancePropertyNames(Class clazz) { + TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz); + if(linker instanceof BeanLinker) { + return ((BeanLinker)linker).getReadablePropertyNames(); + } + return Collections.emptySet(); + } + + /** + * Returns a collection of names of all writable instance properties of a class. + * @param clazz the class + * @return a collection of names of all writable instance properties of a class. + */ + public static Collection getWritableInstancePropertyNames(Class clazz) { + TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz); + if(linker instanceof BeanLinker) { + return ((BeanLinker)linker).getWritablePropertyNames(); + } + return Collections.emptySet(); + } + + /** + * Returns a collection of names of all instance methods of a class. + * @param clazz the class + * @return a collection of names of all instance methods of a class. + */ + public static Collection getInstanceMethodNames(Class clazz) { + TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz); + if(linker instanceof BeanLinker) { + return ((BeanLinker)linker).getMethodNames(); + } + return Collections.emptySet(); + } + + /** + * Returns a collection of names of all readable static properties of a class. + * @param clazz the class + * @return a collection of names of all readable static properties of a class. + */ + public static Collection getReadableStaticPropertyNames(Class clazz) { + return StaticClassLinker.getReadableStaticPropertyNames(clazz); + } + + /** + * Returns a collection of names of all writable static properties of a class. + * @param clazz the class + * @return a collection of names of all writable static properties of a class. + */ + public static Collection getWritableStaticPropertyNames(Class clazz) { + return StaticClassLinker.getWritableStaticPropertyNames(clazz); + } + + /** + * Returns a collection of names of all static methods of a class. + * @param clazz the class + * @return a collection of names of all static methods of a class. + */ + public static Collection getStaticMethodNames(Class clazz) { + return StaticClassLinker.getStaticMethodNames(clazz); + } + @Override public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices) throws Exception { diff --git a/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDetector.java b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDetector.java new file mode 100644 index 00000000000..466bafe65dc --- /dev/null +++ b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDetector.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2010, 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2009-2013 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache 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. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink.beans; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import sun.reflect.CallerSensitive; + +/** + * Utility class that determines if a method or constructor is caller sensitive. It actually encapsulates two different + * strategies for determining caller sensitivity; a more robust one that works if Dynalink runs as code with access + * to {@code sun.reflect} package, and an unprivileged one that is used when Dynalink doesn't have access to that + * package. Note that even the unprivileged strategy is ordinarily robust, but it relies on the {@code toString} method + * of the annotation. If an attacker were to use a different annotation to spoof the string representation of the + * {@code CallerSensitive} annotation, they could designate their own methods as caller sensitive. This however does not + * escalate privileges, only causes Dynalink to never cache method handles for such methods, so all it would do would + * decrease the performance in linking such methods. In the opposite case when an attacker could trick Dynalink into not + * recognizing genuine {@code CallerSensitive} annotations, Dynalink would treat caller sensitive methods as ordinary + * methods, and would cache them bound to a zero-privilege delegate as the caller (just what Dynalink did before it + * could handle caller-sensitive methods). That would practically render caller-sensitive methods exposed through + * Dynalink unusable, but again, can not lead to any privilege escalations. Therefore, even the less robust unprivileged + * strategy is safe; the worst thing a successful attack against it can achieve is slight reduction in Dynalink-exposed + * functionality or performance. + */ +public class CallerSensitiveDetector { + + private static final DetectionStrategy DETECTION_STRATEGY = getDetectionStrategy(); + + static boolean isCallerSensitive(AccessibleObject ao) { + return DETECTION_STRATEGY.isCallerSensitive(ao); + } + + private static DetectionStrategy getDetectionStrategy() { + try { + return new PrivilegedDetectionStrategy(); + } catch(Throwable t) { + return new UnprivilegedDetectionStrategy(); + } + } + + private abstract static class DetectionStrategy { + abstract boolean isCallerSensitive(AccessibleObject ao); + } + + private static class PrivilegedDetectionStrategy extends DetectionStrategy { + private static final Class CALLER_SENSITIVE_ANNOTATION_CLASS = CallerSensitive.class; + + @Override + boolean isCallerSensitive(AccessibleObject ao) { + return ao.getAnnotation(CALLER_SENSITIVE_ANNOTATION_CLASS) != null; + } + } + + private static class UnprivilegedDetectionStrategy extends DetectionStrategy { + private static final String CALLER_SENSITIVE_ANNOTATION_STRING = "@sun.reflect.CallerSensitive()"; + + @Override + boolean isCallerSensitive(AccessibleObject o) { + for(Annotation a: o.getAnnotations()) { + if(String.valueOf(a).equals(CALLER_SENSITIVE_ANNOTATION_STRING)) { + return true; + } + } + return false; + } + } +} diff --git a/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java new file mode 100644 index 00000000000..1e274d516e9 --- /dev/null +++ b/nashorn/src/jdk/internal/dynalink/beans/CallerSensitiveDynamicMethod.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2010, 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2009-2013 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache 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. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink.beans; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import jdk.internal.dynalink.support.Lookup; + +/** + * A dynamic method bound to exactly one Java method or constructor that is caller sensitive. Since the target method is + * caller sensitive, it doesn't cache a method handle but rather uses the passed lookup object in + * {@link #getTarget(java.lang.invoke.MethodHandles.Lookup)} to unreflect a method handle from the reflective member on + * every request. + * + * @author Attila Szegedi + */ +class CallerSensitiveDynamicMethod extends SingleDynamicMethod { + // Typed as "AccessibleObject" as it can be either a method or a constructor. + // If we were Java8-only, we could use java.lang.reflect.Executable + private final AccessibleObject target; + private final MethodType type; + + public CallerSensitiveDynamicMethod(AccessibleObject target) { + super(getName(target)); + this.target = target; + this.type = getMethodType(target); + } + + private static String getName(AccessibleObject target) { + final Member m = (Member)target; + return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(), + m.getName())); + } + + @Override + MethodType getMethodType() { + return type; + } + + private static MethodType getMethodType(AccessibleObject ao) { + final boolean isMethod = ao instanceof Method; + final Class rtype = isMethod ? ((Method)ao).getReturnType() : ((Constructor)ao).getDeclaringClass(); + final Class[] ptypes = isMethod ? ((Method)ao).getParameterTypes() : ((Constructor)ao).getParameterTypes(); + final MethodType type = MethodType.methodType(rtype, ptypes); + final Member m = (Member)ao; + return type.insertParameterTypes(0, + isMethod ? + Modifier.isStatic(m.getModifiers()) ? + Object.class : + m.getDeclaringClass() : + StaticClass.class); + } + + @Override + boolean isVarArgs() { + return target instanceof Method ? ((Method)target).isVarArgs() : ((Constructor)target).isVarArgs(); + } + + @Override + MethodHandle getTarget(MethodHandles.Lookup lookup) { + if(target instanceof Method) { + final MethodHandle mh = Lookup.unreflect(lookup, (Method)target); + if(Modifier.isStatic(((Member)target).getModifiers())) { + return StaticClassIntrospector.editStaticMethodHandle(mh); + } + return mh; + } + return StaticClassIntrospector.editConstructorMethodHandle(Lookup.unreflectConstructor(lookup, + (Constructor)target)); + } +} diff --git a/nashorn/src/jdk/internal/dynalink/beans/ClassString.java b/nashorn/src/jdk/internal/dynalink/beans/ClassString.java index dfcb378662f..8ab45727de6 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/ClassString.java +++ b/nashorn/src/jdk/internal/dynalink/beans/ClassString.java @@ -155,8 +155,8 @@ final class ClassString { } List getMaximallySpecifics(List methods, LinkerServices linkerServices, boolean varArg) { - return MaximallySpecific.getMaximallySpecificMethods(getApplicables(methods, linkerServices, varArg), varArg, - classes, linkerServices); + return MaximallySpecific.getMaximallySpecificMethodHandles(getApplicables(methods, linkerServices, varArg), + varArg, classes, linkerServices); } /** diff --git a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java index aee69ff722e..6beb92b12f7 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java +++ b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethod.java @@ -84,8 +84,7 @@ package jdk.internal.dynalink.beans; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodType; -import java.util.StringTokenizer; +import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.LinkerServices; /** @@ -116,45 +115,28 @@ abstract class DynamicMethod { * is a variable arguments (vararg) method, it will pack the extra arguments in an array before the invocation of * the underlying method if it is not already done. * - * @param callSiteType the method type at a call site + * @param callSiteDescriptor the descriptor of the call site * @param linkerServices linker services. Used for language-specific type conversions. * @return an invocation suitable for calling the method from the specified call site. */ - abstract MethodHandle getInvocation(MethodType callSiteType, LinkerServices linkerServices); + abstract MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices); /** - * Returns a simple dynamic method representing a single underlying Java method (possibly selected among several + * Returns a single dynamic method representing a single underlying Java method (possibly selected among several * overloads) with formal parameter types exactly matching the passed signature. * @param paramTypes the comma-separated list of requested parameter type names. The names will match both * qualified and unqualified type names. - * @return a simple dynamic method representing a single underlying Java method, or null if none of the Java methods + * @return a single dynamic method representing a single underlying Java method, or null if none of the Java methods * behind this dynamic method exactly match the requested parameter types. */ - abstract SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes); + abstract SingleDynamicMethod getMethodForExactParamTypes(String paramTypes); /** - * True if this dynamic method already contains a method handle with an identical signature as the passed in method - * handle. - * @param mh the method handle to check - * @return true if it already contains an equivalent method handle. + * True if this dynamic method already contains a method with an identical signature as the passed in method. + * @param method the method to check + * @return true if it already contains an equivalent method. */ - abstract boolean contains(MethodHandle mh); - - static boolean typeMatchesDescription(String paramTypes, MethodType type) { - final StringTokenizer tok = new StringTokenizer(paramTypes, ", "); - for(int i = 1; i < type.parameterCount(); ++i) { // i = 1 as we ignore the receiver - if(!(tok.hasMoreTokens() && typeNameMatches(tok.nextToken(), type.parameterType(i)))) { - return false; - } - } - return !tok.hasMoreTokens(); - } - - private static boolean typeNameMatches(String typeName, Class type) { - final int lastDot = typeName.lastIndexOf('.'); - final String fullTypeName = type.getCanonicalName(); - return lastDot != -1 && fullTypeName.endsWith(typeName.substring(lastDot)) || typeName.equals(fullTypeName); - } + abstract boolean contains(SingleDynamicMethod method); static String getClassAndMethodName(Class clazz, String name) { final String clazzName = clazz.getCanonicalName(); diff --git a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java index d8ceeea0045..32942b9a423 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java +++ b/nashorn/src/jdk/internal/dynalink/beans/DynamicMethodLinker.java @@ -85,12 +85,12 @@ package jdk.internal.dynalink.beans; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; +import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.internal.dynalink.support.Guards; /** @@ -110,19 +110,18 @@ class DynamicMethodLinker implements TypeBasedGuardingDynamicLinker { return null; } final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor(); - if(desc.getNameTokenCount() != 2 && desc.getNameToken(CallSiteDescriptor.SCHEME) != "dyn") { + if(desc.getNameTokenCount() != 2 && desc.getNameToken(CallSiteDescriptor.SCHEME) != "dyn") { return null; } final String operator = desc.getNameToken(CallSiteDescriptor.OPERATOR); if(operator == "call") { - final MethodType type = desc.getMethodType(); - final MethodHandle invocation = ((DynamicMethod)receiver).getInvocation(type.dropParameterTypes(0, 1), - linkerServices); + final MethodHandle invocation = ((DynamicMethod)receiver).getInvocation( + CallSiteDescriptorFactory.dropParameterTypes(desc, 0, 1), linkerServices); if(invocation == null) { return null; } - return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0, type.parameterType(0)), - Guards.getIdentityGuard(receiver)); + return new GuardedInvocation(MethodHandles.dropArguments(invocation, 0, + desc.getMethodType().parameterType(0)), Guards.getIdentityGuard(receiver)); } return null; } diff --git a/nashorn/src/jdk/internal/dynalink/beans/FacetIntrospector.java b/nashorn/src/jdk/internal/dynalink/beans/FacetIntrospector.java index f4fbd8244f1..97e431ca24b 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/FacetIntrospector.java +++ b/nashorn/src/jdk/internal/dynalink/beans/FacetIntrospector.java @@ -167,10 +167,6 @@ abstract class FacetIntrospector { return editMethodHandle(SafeUnreflector.unreflectSetter(field)); } - MethodHandle unreflect(Method method) { - return editMethodHandle(SafeUnreflector.unreflect(method)); - } - /** * Returns an edited method handle. A facet might need to edit an unreflected method handle before it is usable with * the facet. By default, returns the passed method handle unchanged. The class' static facet will introduce a diff --git a/nashorn/src/jdk/internal/dynalink/beans/MaximallySpecific.java b/nashorn/src/jdk/internal/dynalink/beans/MaximallySpecific.java index 182fd01695d..3ee8e41ab30 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/MaximallySpecific.java +++ b/nashorn/src/jdk/internal/dynalink/beans/MaximallySpecific.java @@ -105,10 +105,58 @@ class MaximallySpecific { * @param varArgs whether to assume the methods are varargs * @return the list of maximally specific methods. */ - static List getMaximallySpecificMethods(List methods, boolean varArgs) { - return getMaximallySpecificMethods(methods, varArgs, null, null); + static List getMaximallySpecificMethods(List methods, boolean varArgs) { + return getMaximallySpecificSingleDynamicMethods(methods, varArgs, null, null); } + private abstract static class MethodTypeGetter { + abstract MethodType getMethodType(T t); + } + + private static final MethodTypeGetter METHOD_HANDLE_TYPE_GETTER = + new MethodTypeGetter() { + @Override + MethodType getMethodType(MethodHandle t) { + return t.type(); + } + }; + + private static final MethodTypeGetter DYNAMIC_METHOD_TYPE_GETTER = + new MethodTypeGetter() { + @Override + MethodType getMethodType(SingleDynamicMethod t) { + return t.getMethodType(); + } + }; + + /** + * Given a list of methods handles, returns a list of maximally specific methods, applying language-runtime + * specific conversion preferences. + * + * @param methods the list of method handles + * @param varArgs whether to assume the method handles are varargs + * @param argTypes concrete argument types for the invocation + * @return the list of maximally specific method handles. + */ + static List getMaximallySpecificMethodHandles(List methods, boolean varArgs, + Class[] argTypes, LinkerServices ls) { + return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, METHOD_HANDLE_TYPE_GETTER); + } + + /** + * Given a list of methods, returns a list of maximally specific methods, applying language-runtime specific + * conversion preferences. + * + * @param methods the list of methods + * @param varArgs whether to assume the methods are varargs + * @param argTypes concrete argument types for the invocation + * @return the list of maximally specific methods. + */ + static List getMaximallySpecificSingleDynamicMethods(List methods, + boolean varArgs, Class[] argTypes, LinkerServices ls) { + return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, DYNAMIC_METHOD_TYPE_GETTER); + } + /** * Given a list of methods, returns a list of maximally specific methods, applying language-runtime specific * conversion preferences. @@ -118,18 +166,18 @@ class MaximallySpecific { * @param argTypes concrete argument types for the invocation * @return the list of maximally specific methods. */ - static List getMaximallySpecificMethods(List methods, boolean varArgs, - Class[] argTypes, LinkerServices ls) { + private static List getMaximallySpecificMethods(List methods, boolean varArgs, + Class[] argTypes, LinkerServices ls, MethodTypeGetter methodTypeGetter) { if(methods.size() < 2) { return methods; } - final LinkedList maximals = new LinkedList<>(); - for(MethodHandle m: methods) { - final MethodType methodType = m.type(); + final LinkedList maximals = new LinkedList<>(); + for(T m: methods) { + final MethodType methodType = methodTypeGetter.getMethodType(m); boolean lessSpecific = false; - for(Iterator maximal = maximals.iterator(); maximal.hasNext();) { - final MethodHandle max = maximal.next(); - switch(isMoreSpecific(methodType, max.type(), varArgs, argTypes, ls)) { + for(Iterator maximal = maximals.iterator(); maximal.hasNext();) { + final T max = maximal.next(); + switch(isMoreSpecific(methodType, methodTypeGetter.getMethodType(max), varArgs, argTypes, ls)) { case TYPE_1_BETTER: { maximal.remove(); break; diff --git a/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java index 7873cf1520e..407d2b8310f 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java +++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedDynamicMethod.java @@ -84,16 +84,21 @@ package jdk.internal.dynalink.beans; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.support.TypeUtilities; /** - * Represents an overloaded method. + * Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all + * constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller + * sensitive methods within the overloads. * * @author Attila Szegedi */ @@ -101,7 +106,7 @@ class OverloadedDynamicMethod extends DynamicMethod { /** * Holds a list of all methods. */ - private final LinkedList methods; + private final LinkedList methods; private final ClassLoader classLoader; /** @@ -111,21 +116,22 @@ class OverloadedDynamicMethod extends DynamicMethod { * @param name the name of the method */ OverloadedDynamicMethod(Class clazz, String name) { - this(new LinkedList(), clazz.getClassLoader(), getClassAndMethodName(clazz, name)); + this(new LinkedList(), clazz.getClassLoader(), getClassAndMethodName(clazz, name)); } - private OverloadedDynamicMethod(LinkedList methods, ClassLoader classLoader, String name) { + private OverloadedDynamicMethod(LinkedList methods, ClassLoader classLoader, String name) { super(name); this.methods = methods; this.classLoader = classLoader; } @Override - SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes) { - final LinkedList matchingMethods = new LinkedList<>(); - for(MethodHandle method: methods) { - if(typeMatchesDescription(paramTypes, method.type())) { - matchingMethods.add(method); + SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) { + final LinkedList matchingMethods = new LinkedList<>(); + for(SingleDynamicMethod method: methods) { + final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes); + if(matchingMethod != null) { + matchingMethods.add(matchingMethod); } } switch(matchingMethods.size()) { @@ -133,8 +139,7 @@ class OverloadedDynamicMethod extends DynamicMethod { return null; } case 1: { - final MethodHandle target = matchingMethods.get(0); - return new SimpleDynamicMethod(target, SimpleDynamicMethod.getMethodNameWithSignature(target, getName())); + return matchingMethods.getFirst(); } default: { throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types " @@ -144,7 +149,8 @@ class OverloadedDynamicMethod extends DynamicMethod { } @Override - public MethodHandle getInvocation(final MethodType callSiteType, final LinkerServices linkerServices) { + public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) { + final MethodType callSiteType = callSiteDescriptor.getMethodType(); // First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2) final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType, ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING); @@ -156,7 +162,7 @@ class OverloadedDynamicMethod extends DynamicMethod { ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY); // Find the methods that are maximally specific based on the call site signature - List maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods(); + List maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods(); if(maximallySpecifics.isEmpty()) { maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods(); if(maximallySpecifics.isEmpty()) { @@ -171,12 +177,12 @@ class OverloadedDynamicMethod extends DynamicMethod { // (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability // rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation. @SuppressWarnings({ "unchecked", "rawtypes" }) - final List invokables = (List)methods.clone(); + final List invokables = (List)methods.clone(); invokables.removeAll(subtypingApplicables.getMethods()); invokables.removeAll(methodInvocationApplicables.getMethods()); invokables.removeAll(variableArityApplicables.getMethods()); - for(final Iterator it = invokables.iterator(); it.hasNext();) { - final MethodHandle m = it.next(); + for(final Iterator it = invokables.iterator(); it.hasNext();) { + final SingleDynamicMethod m = it.next(); if(!isApplicableDynamically(linkerServices, callSiteType, m)) { it.remove(); } @@ -199,54 +205,45 @@ class OverloadedDynamicMethod extends DynamicMethod { } case 1: { // Very lucky, we ended up with a single candidate method handle based on the call site signature; we - // can link it very simply by delegating to a SimpleDynamicMethod. - final MethodHandle mh = invokables.iterator().next(); - return new SimpleDynamicMethod(mh).getInvocation(callSiteType, linkerServices); + // can link it very simply by delegating to the SingleDynamicMethod. + invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices); } default: { // We have more than one candidate. We have no choice but to link to a method that resolves overloads on // every invocation (alternatively, we could opportunistically link the one method that resolves for the // current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd - // go back all the way to candidate selection. - // TODO: cache per call site type - return new OverloadedMethod(invokables, this, callSiteType, linkerServices).getInvoker(); + // go back all the way to candidate selection. Note that we're resolving any potential caller sensitive + // methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it + // has an already determined Lookup. + final List methodHandles = new ArrayList<>(invokables.size()); + final MethodHandles.Lookup lookup = callSiteDescriptor.getLookup(); + for(SingleDynamicMethod method: invokables) { + methodHandles.add(method.getTarget(lookup)); + } + return new OverloadedMethod(methodHandles, this, callSiteType, linkerServices).getInvoker(); } } } @Override - public boolean contains(MethodHandle mh) { - final MethodType type = mh.type(); - for(MethodHandle method: methods) { - if(typesEqualNoReceiver(type, method.type())) { + public boolean contains(SingleDynamicMethod m) { + for(SingleDynamicMethod method: methods) { + if(method.contains(m)) { return true; } } return false; } - private static boolean typesEqualNoReceiver(MethodType type1, MethodType type2) { - final int pc = type1.parameterCount(); - if(pc != type2.parameterCount()) { - return false; - } - for(int i = 1; i < pc; ++i) { // i = 1: ignore receiver - if(type1.parameterType(i) != type2.parameterType(i)) { - return false; - } - } - return true; - } - ClassLoader getClassLoader() { return classLoader; } private static boolean isApplicableDynamically(LinkerServices linkerServices, MethodType callSiteType, - MethodHandle m) { - final MethodType methodType = m.type(); - final boolean varArgs = m.isVarargsCollector(); + SingleDynamicMethod m) { + final MethodType methodType = m.getMethodType(); + final boolean varArgs = m.isVarArgs(); final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0); final int callSiteArgLen = callSiteType.parameterCount(); @@ -300,21 +297,12 @@ class OverloadedDynamicMethod extends DynamicMethod { return new ApplicableOverloadedMethods(methods, callSiteType, test); } - /** - * Add a method identified by a {@link SimpleDynamicMethod} to this overloaded method's set. - * - * @param method the method to add. - */ - void addMethod(SimpleDynamicMethod method) { - addMethod(method.getTarget()); - } - /** * Add a method to this overloaded method's set. * * @param method a method to add */ - public void addMethod(MethodHandle method) { + public void addMethod(SingleDynamicMethod method) { methods.add(method); } } diff --git a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java index 7093e757497..f711489b037 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java +++ b/nashorn/src/jdk/internal/dynalink/beans/OverloadedMethod.java @@ -135,7 +135,7 @@ class OverloadedMethod { varArgMethods.trimToSize(); final MethodHandle bound = SELECT_METHOD.bindTo(this); - final MethodHandle collecting = SimpleDynamicMethod.collectArguments(bound, argNum).asType( + final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType( callSiteType.changeReturnType(MethodHandle.class)); invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting); } @@ -167,7 +167,7 @@ class OverloadedMethod { break; } case 1: { - method = new SimpleDynamicMethod(methods.get(0)).getInvocation(callSiteType, linkerServices); + method = SingleDynamicMethod.getInvocation(methods.get(0), callSiteType, linkerServices); break; } default: { diff --git a/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java index 1fbf7dbb1a0..9d4d6961081 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java +++ b/nashorn/src/jdk/internal/dynalink/beans/SimpleDynamicMethod.java @@ -84,28 +84,21 @@ package jdk.internal.dynalink.beans; import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; -import java.lang.reflect.Array; -import jdk.internal.dynalink.linker.LinkerServices; -import jdk.internal.dynalink.support.Guards; /** - * A dynamic method bound to exactly one, non-overloaded Java method. Handles varargs. + * A dynamic method bound to exactly one Java method or constructor that is not caller sensitive. Since its target is + * not caller sensitive, this class pre-caches its method handle and always returns it from the call to + * {@link #getTarget(Lookup)}. Can be used in general to represents dynamic methods bound to a single method handle, + * even if that handle is not mapped to a Java method, i.e. as a wrapper around field getters/setters, array element + * getters/setters, etc. * * @author Attila Szegedi */ -class SimpleDynamicMethod extends DynamicMethod { +class SimpleDynamicMethod extends SingleDynamicMethod { private final MethodHandle target; - /** - * Creates a simple dynamic method with no name. - * @param target the target method handle - */ - SimpleDynamicMethod(MethodHandle target) { - this(target, null); - } - /** * Creates a new simple dynamic method, with a name constructed from the class name, method name, and handle * signature. @@ -115,125 +108,26 @@ class SimpleDynamicMethod extends DynamicMethod { * @param name the simple name of the method */ SimpleDynamicMethod(MethodHandle target, Class clazz, String name) { - this(target, getName(target, clazz, name)); - } - - SimpleDynamicMethod(MethodHandle target, String name) { - super(name); + super(getName(target, clazz, name)); this.target = target; } private static String getName(MethodHandle target, Class clazz, String name) { - return getMethodNameWithSignature(target, getClassAndMethodName(clazz, name)); + return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name)); } - static String getMethodNameWithSignature(MethodHandle target, String methodName) { - final String typeStr = target.type().toString(); - final int retTypeIndex = typeStr.lastIndexOf(')') + 1; - int secondParamIndex = typeStr.indexOf(',') + 1; - if(secondParamIndex == 0) { - secondParamIndex = retTypeIndex - 1; - } - return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex); + @Override + boolean isVarArgs() { + return target.isVarargsCollector(); } - /** - * Returns the target of this dynamic method - * - * @return the target of this dynamic method - */ - MethodHandle getTarget() { + @Override + MethodType getMethodType() { + return target.type(); + } + + @Override + MethodHandle getTarget(Lookup lookup) { return target; } - - @Override - SimpleDynamicMethod getMethodForExactParamTypes(String paramTypes) { - return typeMatchesDescription(paramTypes, target.type()) ? this : null; - } - - @Override - MethodHandle getInvocation(MethodType callSiteType, LinkerServices linkerServices) { - final MethodType methodType = target.type(); - final int paramsLen = methodType.parameterCount(); - final boolean varArgs = target.isVarargsCollector(); - final MethodHandle fixTarget = varArgs ? target.asFixedArity() : target; - final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen; - final int argsLen = callSiteType.parameterCount(); - if(argsLen < fixParamsLen) { - // Less actual arguments than number of fixed declared arguments; can't invoke. - return null; - } - // Method handle has the same number of fixed arguments as the call site type - if(argsLen == fixParamsLen) { - // Method handle that matches the number of actual arguments as the number of fixed arguments - final MethodHandle matchedMethod; - if(varArgs) { - // If vararg, add a zero-length array of the expected type as the last argument to signify no variable - // arguments. - matchedMethod = MethodHandles.insertArguments(fixTarget, fixParamsLen, Array.newInstance( - methodType.parameterType(fixParamsLen).getComponentType(), 0)); - } else { - // Otherwise, just use the method - matchedMethod = fixTarget; - } - return createConvertingInvocation(matchedMethod, linkerServices, callSiteType); - } - - // What's below only works for varargs - if(!varArgs) { - return null; - } - - final Class varArgType = methodType.parameterType(fixParamsLen); - // Handle a somewhat sinister corner case: caller passes exactly one argument in the vararg position, and we - // must handle both a prepacked vararg array as well as a genuine 1-long vararg sequence. - if(argsLen == paramsLen) { - final Class callSiteLastArgType = callSiteType.parameterType(fixParamsLen); - if(varArgType.isAssignableFrom(callSiteLastArgType)) { - // Call site signature guarantees we'll always be passed a single compatible array; just link directly - // to the method. - return createConvertingInvocation(fixTarget, linkerServices, callSiteType); - } - if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) { - // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive); - // link immediately to a vararg-packing method handle. - return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType); - } - // Call site signature makes no guarantees that the single argument in the vararg position will be - // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg - // method when it is not. - return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType), - createConvertingInvocation(fixTarget, linkerServices, callSiteType), - createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType)); - } - - // Remaining case: more than one vararg. - return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType); - } - - @Override - public boolean contains(MethodHandle mh) { - return target.type().parameterList().equals(mh.type().parameterList()); - } - - /** - * Creates a method handle out of the original target that will collect the varargs for the exact component type of - * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs - * for which it is necessary when later passed to linkerServices.convertArguments(). - * - * @param target the original method handle - * @param parameterCount the total number of arguments in the new method handle - * @return a collecting method handle - */ - static MethodHandle collectArguments(MethodHandle target, final int parameterCount) { - final MethodType methodType = target.type(); - final int fixParamsLen = methodType.parameterCount() - 1; - final Class arrayType = methodType.parameterType(fixParamsLen); - return target.asCollector(arrayType, parameterCount - fixParamsLen); - } - - private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod, - final LinkerServices linkerServices, final MethodType callSiteType) { - return linkerServices.asType(sizedMethod, callSiteType); - } } diff --git a/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java b/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java new file mode 100644 index 00000000000..d15fab9966e --- /dev/null +++ b/nashorn/src/jdk/internal/dynalink/beans/SingleDynamicMethod.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2010, 2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2009-2013 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache 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. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink.beans; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Array; +import java.util.StringTokenizer; +import jdk.internal.dynalink.CallSiteDescriptor; +import jdk.internal.dynalink.linker.LinkerServices; +import jdk.internal.dynalink.support.Guards; + +/** + * Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the + * target method to a call site type (including mapping variable arity methods to a call site signature with different + * arity). + * @author Attila Szegedi + * @version $Id: $ + */ +abstract class SingleDynamicMethod extends DynamicMethod { + SingleDynamicMethod(String name) { + super(name); + } + + /** + * Returns true if this method is variable arity. + * @return true if this method is variable arity. + */ + abstract boolean isVarArgs(); + + /** + * Returns this method's native type. + * @return this method's native type. + */ + abstract MethodType getMethodType(); + + /** + * Given a specified lookup, returns a method handle to this method's target. + * @param lookup the lookup to use. + * @return the handle to this method's target method. + */ + abstract MethodHandle getTarget(MethodHandles.Lookup lookup); + + @Override + MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) { + return getInvocation(getTarget(callSiteDescriptor.getLookup()), callSiteDescriptor.getMethodType(), + linkerServices); + } + + @Override + SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) { + return typeMatchesDescription(paramTypes, getMethodType()) ? this : null; + } + + @Override + boolean contains(SingleDynamicMethod method) { + return getMethodType().parameterList().equals(method.getMethodType().parameterList()); + } + + static String getMethodNameWithSignature(MethodType type, String methodName) { + final String typeStr = type.toString(); + final int retTypeIndex = typeStr.lastIndexOf(')') + 1; + int secondParamIndex = typeStr.indexOf(',') + 1; + if(secondParamIndex == 0) { + secondParamIndex = retTypeIndex - 1; + } + return typeStr.substring(retTypeIndex) + " " + methodName + "(" + typeStr.substring(secondParamIndex, retTypeIndex); + } + + /** + * Given a method handle and a call site type, adapts the method handle to the call site type. Performs type + * conversions as needed using the specified linker services, and in case that the method handle is a vararg + * collector, matches it to the arity of the call site. + * @param target the method handle to adapt + * @param callSiteType the type of the call site + * @param linkerServices the linker services used for type conversions + * @return the adapted method handle. + */ + static MethodHandle getInvocation(MethodHandle target, MethodType callSiteType, LinkerServices linkerServices) { + final MethodType methodType = target.type(); + final int paramsLen = methodType.parameterCount(); + final boolean varArgs = target.isVarargsCollector(); + final MethodHandle fixTarget = varArgs ? target.asFixedArity() : target; + final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen; + final int argsLen = callSiteType.parameterCount(); + if(argsLen < fixParamsLen) { + // Less actual arguments than number of fixed declared arguments; can't invoke. + return null; + } + // Method handle has the same number of fixed arguments as the call site type + if(argsLen == fixParamsLen) { + // Method handle that matches the number of actual arguments as the number of fixed arguments + final MethodHandle matchedMethod; + if(varArgs) { + // If vararg, add a zero-length array of the expected type as the last argument to signify no variable + // arguments. + matchedMethod = MethodHandles.insertArguments(fixTarget, fixParamsLen, Array.newInstance( + methodType.parameterType(fixParamsLen).getComponentType(), 0)); + } else { + // Otherwise, just use the method + matchedMethod = fixTarget; + } + return createConvertingInvocation(matchedMethod, linkerServices, callSiteType); + } + + // What's below only works for varargs + if(!varArgs) { + return null; + } + + final Class varArgType = methodType.parameterType(fixParamsLen); + // Handle a somewhat sinister corner case: caller passes exactly one argument in the vararg position, and we + // must handle both a prepacked vararg array as well as a genuine 1-long vararg sequence. + if(argsLen == paramsLen) { + final Class callSiteLastArgType = callSiteType.parameterType(fixParamsLen); + if(varArgType.isAssignableFrom(callSiteLastArgType)) { + // Call site signature guarantees we'll always be passed a single compatible array; just link directly + // to the method, introducing necessary conversions. Also, preserve it being a variable arity method. + return createConvertingInvocation(target, linkerServices, callSiteType).asVarargsCollector( + callSiteLastArgType); + } + if(!linkerServices.canConvert(callSiteLastArgType, varArgType)) { + // Call site signature guarantees the argument can definitely not be an array (i.e. it is primitive); + // link immediately to a vararg-packing method handle. + return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType); + } + // Call site signature makes no guarantees that the single argument in the vararg position will be + // compatible across all invocations. Need to insert an appropriate guard and fall back to generic vararg + // method when it is not. + return MethodHandles.guardWithTest(Guards.isInstance(varArgType, fixParamsLen, callSiteType), + createConvertingInvocation(fixTarget, linkerServices, callSiteType), + createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType)); + } + + // Remaining case: more than one vararg. + return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType); + } + + /** + * Creates a method handle out of the original target that will collect the varargs for the exact component type of + * the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs + * for which it is necessary when later passed to linkerServices.convertArguments(). + * + * @param target the original method handle + * @param parameterCount the total number of arguments in the new method handle + * @return a collecting method handle + */ + static MethodHandle collectArguments(MethodHandle target, final int parameterCount) { + final MethodType methodType = target.type(); + final int fixParamsLen = methodType.parameterCount() - 1; + final Class arrayType = methodType.parameterType(fixParamsLen); + return target.asCollector(arrayType, parameterCount - fixParamsLen); + } + + private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod, + final LinkerServices linkerServices, final MethodType callSiteType) { + return linkerServices.asType(sizedMethod, callSiteType); + } + + private static boolean typeMatchesDescription(String paramTypes, MethodType type) { + final StringTokenizer tok = new StringTokenizer(paramTypes, ", "); + for(int i = 1; i < type.parameterCount(); ++i) { // i = 1 as we ignore the receiver + if(!(tok.hasMoreTokens() && typeNameMatches(tok.nextToken(), type.parameterType(i)))) { + return false; + } + } + return !tok.hasMoreTokens(); + } + + private static boolean typeNameMatches(String typeName, Class type) { + return typeName.equals(typeName.indexOf('.') == -1 ? type.getSimpleName() : type.getCanonicalName()); + } +} diff --git a/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java b/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java index 214152a41ec..62ce41a95a6 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java +++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassIntrospector.java @@ -106,10 +106,18 @@ class StaticClassIntrospector extends FacetIntrospector { @Override MethodHandle editMethodHandle(MethodHandle mh) { + return editStaticMethodHandle(mh); + } + + static MethodHandle editStaticMethodHandle(MethodHandle mh) { return dropReceiver(mh, Object.class); } - static MethodHandle dropReceiver(final MethodHandle mh, final Class receiverClass) { + static MethodHandle editConstructorMethodHandle(MethodHandle cmh) { + return dropReceiver(cmh, StaticClass.class); + } + + private static MethodHandle dropReceiver(final MethodHandle mh, final Class receiverClass) { MethodHandle newHandle = MethodHandles.dropArguments(mh, 0, receiverClass); // NOTE: this is a workaround for the fact that dropArguments doesn't preserve vararg collector state. if(mh.isVarargsCollector() && !newHandle.isVarargsCollector()) { diff --git a/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java b/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java index d6096fe559b..2abdbbe99e1 100644 --- a/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java +++ b/nashorn/src/jdk/internal/dynalink/beans/StaticClassLinker.java @@ -87,13 +87,11 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; +import java.util.Collection; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType; import jdk.internal.dynalink.linker.GuardedInvocation; -import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; @@ -104,9 +102,9 @@ import jdk.internal.dynalink.support.Lookup; * @author Attila Szegedi */ class StaticClassLinker implements TypeBasedGuardingDynamicLinker { - private final ClassValue linkers = new ClassValue() { + private static final ClassValue linkers = new ClassValue() { @Override - protected GuardingDynamicLinker computeValue(Class clazz) { + protected SingleClassStaticsLinker computeValue(Class clazz) { return new SingleClassStaticsLinker(clazz); } }; @@ -131,20 +129,11 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker { private static DynamicMethod createConstructorMethod(Class clazz) { if(clazz.isArray()) { final MethodHandle boundArrayCtor = ARRAY_CTOR.bindTo(clazz.getComponentType()); - return new SimpleDynamicMethod(drop(boundArrayCtor.asType(boundArrayCtor.type().changeReturnType( - clazz))), clazz, ""); + return new SimpleDynamicMethod(StaticClassIntrospector.editConstructorMethodHandle( + boundArrayCtor.asType(boundArrayCtor.type().changeReturnType(clazz))), clazz, ""); } - final Constructor[] ctrs = clazz.getConstructors(); - final List mhs = new ArrayList<>(ctrs.length); - for(int i = 0; i < ctrs.length; ++i) { - mhs.add(drop(SafeUnreflector.unreflectConstructor(ctrs[i]))); - } - return createDynamicMethod(mhs, clazz, ""); - } - - private static MethodHandle drop(MethodHandle mh) { - return StaticClassIntrospector.dropReceiver(mh, StaticClass.class); + return createDynamicMethod(Arrays.asList(clazz.getConstructors()), clazz, ""); } @Override @@ -161,17 +150,28 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker { } final CallSiteDescriptor desc = request.getCallSiteDescriptor(); final String op = desc.getNameToken(CallSiteDescriptor.OPERATOR); - final MethodType methodType = desc.getMethodType(); if("new" == op && constructor != null) { - final MethodHandle ctorInvocation = constructor.getInvocation(methodType, linkerServices); + final MethodHandle ctorInvocation = constructor.getInvocation(desc, linkerServices); if(ctorInvocation != null) { - return new GuardedInvocation(ctorInvocation, getClassGuard(methodType)); + return new GuardedInvocation(ctorInvocation, getClassGuard(desc.getMethodType())); } } return null; } } + static Collection getReadableStaticPropertyNames(Class clazz) { + return linkers.get(clazz).getReadablePropertyNames(); + } + + static Collection getWritableStaticPropertyNames(Class clazz) { + return linkers.get(clazz).getWritablePropertyNames(); + } + + static Collection getStaticMethodNames(Class clazz) { + return linkers.get(clazz).getMethodNames(); + } + @Override public GuardedInvocation getGuardedInvocation(LinkRequest request, LinkerServices linkerServices) throws Exception { final Object receiver = request.getReceiver(); diff --git a/nashorn/src/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java b/nashorn/src/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java index e51f6fe2f2a..3161cf50a76 100644 --- a/nashorn/src/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java +++ b/nashorn/src/jdk/internal/dynalink/support/AbstractCallSiteDescriptor.java @@ -139,8 +139,9 @@ public abstract class AbstractCallSiteDescriptor implements CallSiteDescriptor { @Override public int hashCode() { + final MethodHandles.Lookup lookup = getLookup(); + int h = lookup.lookupClass().hashCode() + 31 * lookup.lookupModes(); final int c = getNameTokenCount(); - int h = 0; for(int i = 0; i < c; ++i) { h = h * 31 + getNameToken(i).hashCode(); } diff --git a/nashorn/src/jdk/internal/dynalink/support/Lookup.java b/nashorn/src/jdk/internal/dynalink/support/Lookup.java index 52a610246a8..4b21e1c4af4 100644 --- a/nashorn/src/jdk/internal/dynalink/support/Lookup.java +++ b/nashorn/src/jdk/internal/dynalink/support/Lookup.java @@ -122,6 +122,18 @@ public class Lookup { * @return the unreflected method handle. */ public MethodHandle unreflect(Method m) { + return unreflect(lookup, m); + } + + /** + * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, converting any encountered + * {@link IllegalAccessException} into an {@link IllegalAccessError}. + * + * @param lookup the lookup used to unreflect + * @param m the method to unreflect + * @return the unreflected method handle. + */ + public static MethodHandle unreflect(MethodHandles.Lookup lookup, Method m) { try { return lookup.unreflect(m); } catch(IllegalAccessException e) { @@ -131,7 +143,6 @@ public class Lookup { } } - /** * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter(Field)}, converting any encountered * {@link IllegalAccessException} into an {@link IllegalAccessError}. @@ -202,6 +213,18 @@ public class Lookup { * @return the unreflected constructor handle. */ public MethodHandle unreflectConstructor(Constructor c) { + return unreflectConstructor(lookup, c); + } + + /** + * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, converting any + * encountered {@link IllegalAccessException} into an {@link IllegalAccessError}. + * + * @param lookup the lookup used to unreflect + * @param c the constructor to unreflect + * @return the unreflected constructor handle. + */ + public static MethodHandle unreflectConstructor(MethodHandles.Lookup lookup, Constructor c) { try { return lookup.unreflectConstructor(c); } catch(IllegalAccessException e) { diff --git a/nashorn/src/jdk/nashorn/api/scripting/NashornException.java b/nashorn/src/jdk/nashorn/api/scripting/NashornException.java index 3cd687cce08..d5ec5bb4a60 100644 --- a/nashorn/src/jdk/nashorn/api/scripting/NashornException.java +++ b/nashorn/src/jdk/nashorn/api/scripting/NashornException.java @@ -146,7 +146,7 @@ public abstract class NashornException extends RuntimeException { * @return array of javascript stack frames */ public static StackTraceElement[] getScriptFrames(final Throwable exception) { - final StackTraceElement[] frames = ((Throwable)exception).getStackTrace(); + final StackTraceElement[] frames = exception.getStackTrace(); final List filtered = new ArrayList<>(); for (final StackTraceElement st : frames) { if (ECMAErrors.isScriptFrame(st)) { @@ -170,7 +170,7 @@ public abstract class NashornException extends RuntimeException { */ public static String getScriptStackString(final Throwable exception) { final StringBuilder buf = new StringBuilder(); - final StackTraceElement[] frames = getScriptFrames((Throwable)exception); + final StackTraceElement[] frames = getScriptFrames(exception); for (final StackTraceElement st : frames) { buf.append("\tat "); buf.append(st.getMethodName()); diff --git a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java index d38e63c88b8..682975a85d5 100644 --- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -33,6 +33,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.net.URL; import java.nio.charset.Charset; import java.security.AccessController; @@ -184,6 +185,19 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } private T getInterfaceInner(final Object self, final Class clazz) { + if (clazz == null || !clazz.isInterface()) { + throw new IllegalArgumentException("interface Class expected"); + } + + // perform security access check as early as possible + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (! Modifier.isPublic(clazz.getModifiers())) { + throw new SecurityException("attempt to implement non-public interfce: " + clazz); + } + Context.checkPackageAccess(clazz.getName()); + } + final ScriptObject realSelf; final ScriptObject ctxtGlobal = getNashornGlobalFrom(context); if(self == null) { @@ -193,6 +207,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } else { realSelf = (ScriptObject)self; } + try { final ScriptObject oldGlobal = getNashornGlobal(); try { diff --git a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java index c7dbab5a184..c7c384e7df5 100644 --- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java +++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java @@ -52,11 +52,6 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { private final ScriptObject sobj; private final ScriptObject global; - ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) { - this.sobj = sobj; - this.global = global; - } - @Override public boolean equals(final Object other) { if (other instanceof ScriptObjectMirror) { @@ -81,25 +76,6 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { }); } - private V inGlobal(final Callable callable) { - final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal(); - final boolean globalChanged = (oldGlobal != global); - if (globalChanged) { - NashornScriptEngine.setNashornGlobal(global); - } - try { - return callable.call(); - } catch (final RuntimeException e) { - throw e; - } catch (final Exception e) { - throw new AssertionError("Cannot happen", e); - } finally { - if (globalChanged) { - NashornScriptEngine.setNashornGlobal(oldGlobal); - } - } - } - // JSObject methods @Override public Object call(final String functionName, final Object... args) { @@ -212,6 +188,8 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { }); } + // javax.script.Bindings methods + @Override public void clear() { inGlobal(new Callable() { @@ -308,9 +286,9 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public void putAll(final Map map) { final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal(); final boolean globalChanged = (oldGlobal != global); - final boolean strict = sobj.isStrictContext(); inGlobal(new Callable() { @Override public Object call() { + final boolean strict = global.isStrictContext(); for (final Map.Entry entry : map.entrySet()) { final Object value = entry.getValue(); final Object modValue = globalChanged? wrap(value, oldGlobal) : value; @@ -379,7 +357,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public Object getProto() { return inGlobal(new Callable() { @Override public Object call() { - return wrap(getScriptObject().getProto(), global); + return wrap(sobj.getProto(), global); } }); } @@ -395,7 +373,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public Object getOwnPropertyDescriptor(final String key) { return inGlobal(new Callable() { @Override public Object call() { - return wrap(getScriptObject().getOwnPropertyDescriptor(key), global); + return wrap(sobj.getOwnPropertyDescriptor(key), global); } }); } @@ -409,7 +387,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public String[] getOwnKeys(final boolean all) { return inGlobal(new Callable() { @Override public String[] call() { - return getScriptObject().getOwnKeys(all); + return sobj.getOwnKeys(all); } }); } @@ -422,7 +400,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public ScriptObjectMirror preventExtensions() { return inGlobal(new Callable() { @Override public ScriptObjectMirror call() { - getScriptObject().preventExtensions(); + sobj.preventExtensions(); return ScriptObjectMirror.this; } }); @@ -435,7 +413,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public boolean isExtensible() { return inGlobal(new Callable() { @Override public Boolean call() { - return getScriptObject().isExtensible(); + return sobj.isExtensible(); } }); } @@ -447,7 +425,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public ScriptObjectMirror seal() { return inGlobal(new Callable() { @Override public ScriptObjectMirror call() { - getScriptObject().seal(); + sobj.seal(); return ScriptObjectMirror.this; } }); @@ -460,7 +438,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public boolean isSealed() { return inGlobal(new Callable() { @Override public Boolean call() { - return getScriptObject().isSealed(); + return sobj.isSealed(); } }); } @@ -472,7 +450,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public ScriptObjectMirror freeze() { return inGlobal(new Callable() { @Override public ScriptObjectMirror call() { - getScriptObject().freeze(); + sobj.freeze(); return ScriptObjectMirror.this; } }); @@ -485,7 +463,7 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { public boolean isFrozen() { return inGlobal(new Callable() { @Override public Boolean call() { - return getScriptObject().isFrozen(); + return sobj.isFrozen(); } }); } @@ -507,11 +485,38 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { return inGlobal(new Callable() { @Override public Boolean call() { - return getScriptObject().isInstance(instance.getScriptObject()); + return sobj.isInstance(instance.sobj); } }); } + /** + * is this a function object? + * + * @return if this mirror wraps a ECMAScript function instance + */ + public boolean isFunction() { + return sobj instanceof ScriptFunction; + } + + /** + * is this a 'use strict' function object? + * + * @return true if this mirror represents a ECMAScript 'use strict' function + */ + public boolean isStrictFunction() { + return isFunction() && ((ScriptFunction)sobj).isStrict(); + } + + /** + * is this an array object? + * + * @return if this mirror wraps a ECMAScript array object + */ + public boolean isArray() { + return sobj.isArray(); + } + /** * Utility to check if given object is ECMAScript undefined value * @@ -522,35 +527,6 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { return obj == ScriptRuntime.UNDEFINED; } - /** - * is this a function object? - * - * @return if this mirror wraps a ECMAScript function instance - */ - public boolean isFunction() { - return getScriptObject() instanceof ScriptFunction; - } - - /** - * is this a 'use strict' function object? - * - * @return true if this mirror represents a ECMAScript 'use strict' function - */ - public boolean isStrictFunction() { - return isFunction() && ((ScriptFunction)getScriptObject()).isStrict(); - } - - /** - * is this an array object? - * - * @return if this mirror wraps a ECMAScript array object - */ - public boolean isArray() { - return getScriptObject().isArray(); - } - - // These are public only so that Context can access these. - /** * Make a script object mirror on given object if needed. * @@ -621,6 +597,12 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { } // package-privates below this. + + ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) { + this.sobj = sobj; + this.global = global; + } + ScriptObject getScriptObject() { return sobj; } @@ -628,4 +610,25 @@ public final class ScriptObjectMirror extends JSObject implements Bindings { static Object translateUndefined(Object obj) { return (obj == ScriptRuntime.UNDEFINED)? null : obj; } + + // internals only below this. + private V inGlobal(final Callable callable) { + final ScriptObject oldGlobal = NashornScriptEngine.getNashornGlobal(); + final boolean globalChanged = (oldGlobal != global); + if (globalChanged) { + NashornScriptEngine.setNashornGlobal(global); + } + try { + return callable.call(); + } catch (final RuntimeException e) { + throw e; + } catch (final Exception e) { + throw new AssertionError("Cannot happen", e); + } finally { + if (globalChanged) { + NashornScriptEngine.setNashornGlobal(oldGlobal); + } + } + } + } diff --git a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java index 3a442a7d8f2..f56a881def6 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java @@ -61,6 +61,7 @@ import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; @@ -72,7 +73,6 @@ import jdk.nashorn.internal.ir.LiteralNode; import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; -import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.RuntimeNode.Request; @@ -94,7 +94,6 @@ import jdk.nashorn.internal.runtime.DebugLogger; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.ScriptObject; /** * This is the attribution pass of the code generator. Attr takes Lowered IR, @@ -166,19 +165,19 @@ final class Attr extends NodeOperatorVisitor { } private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) { - initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL, FunctionNode.FUNCTION_TYPE); + initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL); initCompileConstant(THIS, body, IS_PARAM | IS_THIS, Type.OBJECT); if (functionNode.isVarArg()) { - initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL, Type.OBJECT_ARRAY); + initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL); if (functionNode.needsArguments()) { - initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED, Type.typeFor(ScriptObject.class)); + initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED); addLocalDef(ARGUMENTS.symbolName()); } } initParameters(functionNode, body); - initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED, Type.typeFor(ScriptObject.class)); + initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED); initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED, Type.OBJECT); } @@ -234,10 +233,25 @@ final class Attr extends NodeOperatorVisitor { @Override public boolean enterVarNode(final VarNode varNode) { final String name = varNode.getName().getName(); - //if this is used the var node symbol needs to be tagged as can be undefined + //if this is used before the var node, the var node symbol needs to be tagged as can be undefined if (uses.contains(name)) { canBeUndefined.add(name); } + + // all uses of the declared varnode inside the var node are potentially undefined + // however this is a bit conservative as e.g. var x = 17; var x = 1 + x; does work + if (!varNode.isFunctionDeclaration() && varNode.getInit() != null) { + varNode.getInit().accept(new NodeVisitor(new LexicalContext()) { + @Override + public boolean enterIdentNode(final IdentNode identNode) { + if (name.equals(identNode.getName())) { + canBeUndefined.add(name); + } + return false; + } + }); + } + return true; } @@ -257,6 +271,7 @@ final class Attr extends NodeOperatorVisitor { } return varNode.setName((IdentNode)ident.setSymbol(lc, symbol)); } + return varNode; } }); @@ -326,10 +341,11 @@ final class Attr extends NodeOperatorVisitor { catchNestingLevel++; // define block-local exception variable - final Symbol def = defineSymbol(block, exception.getName(), IS_VAR | IS_LET | IS_ALWAYS_DEFINED); + final String exname = exception.getName(); + final Symbol def = defineSymbol(block, exname, IS_VAR | IS_LET | IS_ALWAYS_DEFINED); newType(def, Type.OBJECT); //we can catch anything, not just ecma exceptions - addLocalDef(exception.getName()); + addLocalDef(exname); return true; } @@ -496,7 +512,6 @@ final class Attr extends NodeOperatorVisitor { assert nameSymbol != null; selfInit = selfInit.setName((IdentNode)name.setSymbol(lc, nameSymbol)); - selfInit = (VarNode)selfInit.setSymbol(lc, nameSymbol); newStatements.add(selfInit); newStatements.addAll(body.getStatements()); @@ -661,7 +676,7 @@ final class Attr extends NodeOperatorVisitor { if (scopeBlock != null) { assert lc.contains(scopeBlock); - lc.setFlag(scopeBlock, Block.NEEDS_SCOPE); + lc.setBlockNeedsScope(scopeBlock); } } } @@ -723,15 +738,10 @@ final class Attr extends NodeOperatorVisitor { return end(ensureSymbol(Type.OBJECT, objectNode)); } - @Override - public Node leavePropertyNode(final PropertyNode propertyNode) { - // assign a pseudo symbol to property name, see NASHORN-710 - return propertyNode.setSymbol(lc, new Symbol(propertyNode.getKeyName(), 0, Type.OBJECT)); - } - @Override public Node leaveReturnNode(final ReturnNode returnNode) { - final Node expr = returnNode.getExpression(); + final Expression expr = returnNode.getExpression(); + final Type returnType; if (expr != null) { //we can't do parameter specialization if we return something that hasn't been typed yet @@ -740,10 +750,12 @@ final class Attr extends NodeOperatorVisitor { symbol.setType(Type.OBJECT); } - final Type returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType()); - returnTypes.push(returnType); - LOG.info("Returntype is now ", returnType); + returnType = Type.widest(returnTypes.pop(), symbol.getSymbolType()); + } else { + returnType = Type.OBJECT; //undefined } + LOG.info("Returntype is now ", returnType); + returnTypes.push(returnType); end(returnNode); @@ -765,7 +777,7 @@ final class Attr extends NodeOperatorVisitor { final LiteralNode lit = (LiteralNode)test; if (lit.isNumeric() && !(lit.getValue() instanceof Integer)) { if (JSType.isRepresentableAsInt(lit.getNumber())) { - newCaseNode = caseNode.setTest(LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); + newCaseNode = caseNode.setTest((Expression)LiteralNode.newInstance(lit, lit.getInt32()).accept(this)); } } } else { @@ -774,6 +786,9 @@ final class Attr extends NodeOperatorVisitor { } type = Type.widest(type, newCaseNode.getTest().getType()); + if (type.isBoolean()) { + type = Type.OBJECT; //booleans and integers aren't assignment compatible + } } newCases.add(newCaseNode); @@ -825,19 +840,18 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveVarNode(final VarNode varNode) { - VarNode newVarNode = varNode; + final Expression init = varNode.getInit(); + final IdentNode ident = varNode.getName(); + final String name = ident.getName(); - final Node init = newVarNode.getInit(); - final IdentNode ident = newVarNode.getName(); - final String name = ident.getName(); - - final Symbol symbol = findSymbol(lc.getCurrentBlock(), ident.getName()); + final Symbol symbol = findSymbol(lc.getCurrentBlock(), name); + assert ident.getSymbol() == symbol; if (init == null) { // var x; with no init will be treated like a use of x by // leaveIdentNode unless we remove the name from the localdef list. removeLocalDef(name); - return end(newVarNode.setSymbol(lc, symbol)); + return end(varNode); } addLocalDef(name); @@ -846,8 +860,7 @@ final class Attr extends NodeOperatorVisitor { final IdentNode newIdent = (IdentNode)ident.setSymbol(lc, symbol); - newVarNode = newVarNode.setName(newIdent); - newVarNode = (VarNode)newVarNode.setSymbol(lc, symbol); + final VarNode newVarNode = varNode.setName(newIdent); final boolean isScript = lc.getDefiningFunction(symbol).isProgram(); //see NASHORN-56 if ((init.getType().isNumeric() || init.getType().isBoolean()) && !isScript) { @@ -857,7 +870,7 @@ final class Attr extends NodeOperatorVisitor { newType(symbol, Type.OBJECT); } - assert newVarNode.hasType() : newVarNode + " has no type"; + assert newVarNode.getName().hasType() : newVarNode + " has no type"; return end(newVarNode); } @@ -885,11 +898,11 @@ final class Attr extends NodeOperatorVisitor { public Node leaveDELETE(final UnaryNode unaryNode) { final FunctionNode currentFunctionNode = lc.getCurrentFunction(); final boolean strictMode = currentFunctionNode.isStrict(); - final Node rhs = unaryNode.rhs(); - final Node strictFlagNode = LiteralNode.newInstance(unaryNode, strictMode).accept(this); + final Expression rhs = unaryNode.rhs(); + final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this); Request request = Request.DELETE; - final List args = new ArrayList<>(); + final List args = new ArrayList<>(); if (rhs instanceof IdentNode) { // If this is a declared variable or a function parameter, delete always fails (except for globals). @@ -900,7 +913,7 @@ final class Attr extends NodeOperatorVisitor { if (failDelete && rhs.getSymbol().isThis()) { return LiteralNode.newInstance(unaryNode, true).accept(this); } - final Node literalNode = LiteralNode.newInstance(unaryNode, name).accept(this); + final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this); if (!failDelete) { args.add(compilerConstant(SCOPE)); @@ -912,16 +925,17 @@ final class Attr extends NodeOperatorVisitor { request = Request.FAIL_DELETE; } } else if (rhs instanceof AccessNode) { - final Node base = ((AccessNode)rhs).getBase(); - final IdentNode property = ((AccessNode)rhs).getProperty(); + final Expression base = ((AccessNode)rhs).getBase(); + final IdentNode property = ((AccessNode)rhs).getProperty(); args.add(base); - args.add(LiteralNode.newInstance(unaryNode, property.getName()).accept(this)); + args.add((Expression)LiteralNode.newInstance(unaryNode, property.getName()).accept(this)); args.add(strictFlagNode); } else if (rhs instanceof IndexNode) { - final Node base = ((IndexNode)rhs).getBase(); - final Node index = ((IndexNode)rhs).getIndex(); + final IndexNode indexNode = (IndexNode)rhs; + final Expression base = indexNode.getBase(); + final Expression index = indexNode.getIndex(); args.add(base); args.add(index); @@ -976,15 +990,15 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveTYPEOF(final UnaryNode unaryNode) { - final Node rhs = unaryNode.rhs(); + final Expression rhs = unaryNode.rhs(); - List args = new ArrayList<>(); + List args = new ArrayList<>(); if (rhs instanceof IdentNode && !rhs.getSymbol().isParam() && !rhs.getSymbol().isVar()) { args.add(compilerConstant(SCOPE)); - args.add(LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null + args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null } else { args.add(rhs); - args.add(LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' + args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this' } RuntimeNode runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args); @@ -1009,10 +1023,7 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveVOID(final UnaryNode unaryNode) { - final RuntimeNode runtimeNode = (RuntimeNode)new RuntimeNode(unaryNode, Request.VOID).accept(this); - assert runtimeNode.getSymbol().getSymbolType().isObject(); - end(unaryNode); - return runtimeNode; + return end(ensureSymbol(Type.OBJECT, unaryNode)); } /** @@ -1021,8 +1032,8 @@ final class Attr extends NodeOperatorVisitor { */ @Override public Node leaveADD(final BinaryNode binaryNode) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); ensureTypeNotUnknown(lhs); ensureTypeNotUnknown(rhs); @@ -1077,8 +1088,8 @@ final class Attr extends NodeOperatorVisitor { private Node leaveAssignmentNode(final BinaryNode binaryNode) { BinaryNode newBinaryNode = binaryNode; - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); final Type type; if (rhs.getType().isNumeric()) { @@ -1114,8 +1125,8 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveASSIGN_ADD(final BinaryNode binaryNode) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); final Type widest = Type.widest(lhs.getType(), rhs.getType()); //Type.NUMBER if we can't prove that the add doesn't overflow. todo @@ -1394,19 +1405,26 @@ final class Attr extends NodeOperatorVisitor { @Override public Node leaveTernaryNode(final TernaryNode ternaryNode) { - final Node lhs = ternaryNode.rhs(); - final Node rhs = ternaryNode.third(); + final Expression trueExpr = ternaryNode.getTrueExpression(); + final Expression falseExpr = ternaryNode.getFalseExpression(); - ensureTypeNotUnknown(lhs); - ensureTypeNotUnknown(rhs); + ensureTypeNotUnknown(trueExpr); + ensureTypeNotUnknown(falseExpr); - final Type type = Type.widest(lhs.getType(), rhs.getType()); + final Type type = Type.widest(trueExpr.getType(), falseExpr.getType()); return end(ensureSymbol(type, ternaryNode)); } + private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) { + final Class type = cc.type(); + // Must not call this method for constants with no explicit types; use the one with (..., Type) signature instead. + assert type != null; + initCompileConstant(cc, block, flags, Type.typeFor(type)); + } + private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags, final Type type) { final Symbol symbol = defineSymbol(block, cc.symbolName(), flags); - newType(symbol, type); + symbol.setTypeOverride(type); symbol.setNeedsSlot(true); } @@ -1511,7 +1529,7 @@ final class Attr extends NodeOperatorVisitor { } } - private static void ensureTypeNotUnknown(final Node node) { + private static void ensureTypeNotUnknown(final Expression node) { final Symbol symbol = node.getSymbol(); @@ -1568,13 +1586,13 @@ final class Attr extends NodeOperatorVisitor { * * @param assignmentDest the destination node of the assignment, e.g. lhs for binary nodes */ - private Node ensureAssignmentSlots(final Node assignmentDest) { + private Expression ensureAssignmentSlots(final Expression assignmentDest) { final LexicalContext attrLexicalContext = lc; - return assignmentDest.accept(new NodeVisitor(new LexicalContext()) { + return (Expression)assignmentDest.accept(new NodeVisitor(new LexicalContext()) { @Override public Node leaveIndexNode(final IndexNode indexNode) { assert indexNode.getSymbol().isTemp(); - final Node index = indexNode.getIndex(); + final Expression index = indexNode.getIndex(); //only temps can be set as needing slots. the others will self resolve //it is illegal to take a scope var and force it to be a slot, that breaks Symbol indexSymbol = index.getSymbol(); @@ -1616,7 +1634,7 @@ final class Attr extends NodeOperatorVisitor { changed.clear(); final FunctionNode newFunctionNode = (FunctionNode)currentFunctionNode.accept(new NodeVisitor(new LexicalContext()) { - private Node widen(final Node node, final Type to) { + private Expression widen(final Expression node, final Type to) { if (node instanceof LiteralNode) { return node; } @@ -1628,7 +1646,7 @@ final class Attr extends NodeOperatorVisitor { symbol = temporarySymbols.getTypedTemporarySymbol(to); } newType(symbol, to); - final Node newNode = node.setSymbol(lc, symbol); + final Expression newNode = node.setSymbol(lc, symbol); changed.add(newNode); return newNode; } @@ -1683,7 +1701,7 @@ final class Attr extends NodeOperatorVisitor { private Node leaveSelfModifyingAssignmentNode(final BinaryNode binaryNode, final Type destType) { //e.g. for -=, Number, no wider, destType (binaryNode.getWidestOperationType()) is the coerce type - final Node lhs = binaryNode.lhs(); + final Expression lhs = binaryNode.lhs(); newType(lhs.getSymbol(), destType); //may not narrow if dest is already wider than destType // ensureSymbol(destType, binaryNode); //for OP= nodes, the node can carry a narrower types than its lhs rhs. This is perfectly fine @@ -1691,9 +1709,9 @@ final class Attr extends NodeOperatorVisitor { return end(ensureSymbol(destType, ensureAssignmentSlots(binaryNode))); } - private Node ensureSymbol(final Type type, final Node node) { + private Expression ensureSymbol(final Type type, final Expression expr) { LOG.info("New TEMPORARY added to ", lc.getCurrentFunction().getName(), " type=", type); - return temporarySymbols.ensureSymbol(lc, type, node); + return temporarySymbols.ensureSymbol(lc, type, expr); } private Symbol newInternal(final String name, final Type type) { @@ -1815,11 +1833,11 @@ final class Attr extends NodeOperatorVisitor { return true; } - private Node end(final Node node) { + private T end(final T node) { return end(node, true); } - private Node end(final Node node, final boolean printNode) { + private T end(final T node, final boolean printNode) { if(node instanceof Statement) { // If we're done with a statement, all temporaries can be reused. temporarySymbols.reuse(); @@ -1834,10 +1852,13 @@ final class Attr extends NodeOperatorVisitor { append(" in '"). append(lc.getCurrentFunction().getName()); - if (node.getSymbol() == null) { - sb.append(" "); - } else { - sb.append(" '); + if(node instanceof Expression) { + final Symbol symbol = ((Expression)node).getSymbol(); + if (symbol == null) { + sb.append(" "); + } else { + sb.append(" '); + } } LOG.unindent(); diff --git a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java index ee922115d15..02ea95f17e6 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java @@ -34,7 +34,7 @@ import static jdk.nashorn.internal.codegen.Condition.NE; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.BinaryNode; -import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.UnaryNode; @@ -52,16 +52,16 @@ final class BranchOptimizer { this.method = method; } - void execute(final Node node, final Label label, final boolean state) { + void execute(final Expression node, final Label label, final boolean state) { branchOptimizer(node, label, state); } - private void load(final Node node) { + private void load(final Expression node) { codegen.load(node); } private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) { - final Node rhs = unaryNode.rhs(); + final Expression rhs = unaryNode.rhs(); switch (unaryNode.tokenType()) { case NOT: @@ -88,8 +88,8 @@ final class BranchOptimizer { } private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); switch (binaryNode.tokenType()) { case AND: @@ -173,7 +173,7 @@ final class BranchOptimizer { } } - private void branchOptimizer(final Node node, final Label label, final boolean state) { + private void branchOptimizer(final Expression node, final Label label, final boolean state) { if (!(node instanceof TernaryNode)) { if (node instanceof BinaryNode) { diff --git a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java index 414d1bb8bc3..9503e95a4b2 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -55,10 +55,12 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.TreeMap; import jdk.nashorn.internal.codegen.ClassEmitter.Flag; import jdk.nashorn.internal.codegen.CompilerConstants.Call; @@ -69,6 +71,7 @@ import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.BreakableNode; import jdk.nashorn.internal.ir.CallNode; @@ -76,7 +79,8 @@ import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.EmptyNode; -import jdk.nashorn.internal.ir.ExecuteNode; +import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; @@ -109,6 +113,8 @@ import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.objects.Global; +import jdk.nashorn.internal.objects.ScriptFunctionImpl; import jdk.nashorn.internal.parser.Lexer.RegexToken; import jdk.nashorn.internal.parser.TokenType; import jdk.nashorn.internal.runtime.Context; @@ -148,11 +154,9 @@ import jdk.nashorn.internal.runtime.linker.LinkerCallSite; */ final class CodeGenerator extends NodeOperatorVisitor { - /** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */ - private static final String GLOBAL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "Global"; + private static final String GLOBAL_OBJECT = Type.getInternalName(Global.class); - /** Name of the ScriptFunctionImpl, cannot be referred to as .class @see FunctionObjectCreator */ - private static final String SCRIPTFUNCTION_IMPL_OBJECT = Compiler.OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl"; + private static final String SCRIPTFUNCTION_IMPL_OBJECT = Type.getInternalName(ScriptFunctionImpl.class); /** Constant data & installation. The only reason the compiler keeps this is because it is assigned * by reflection in class installation */ @@ -179,6 +183,10 @@ final class CodeGenerator extends NodeOperatorVisitor emittedMethods = new HashSet<>(); /** * Constructor. @@ -348,11 +356,11 @@ final class CodeGenerator extends NodeOperatorVisitor args) { + private int loadArgs(final List args) { return loadArgs(args, null, false, args.size()); } - private int loadArgs(final List args, final String signature, final boolean isVarArg, final int argCount) { + private int loadArgs(final List args, final String signature, final boolean isVarArg, final int argCount) { // arg have already been converted to objects here. if (isVarArg || argCount > LinkerCallSite.ARGLIMIT) { loadArgsArray(args); @@ -551,7 +562,7 @@ final class CodeGenerator extends NodeOperatorVisitor= argCount) { @@ -572,12 +583,13 @@ final class CodeGenerator extends NodeOperatorVisitor args = callNode.getArgs(); - final Node function = callNode.getFunction(); - final Block currentBlock = lc.getCurrentBlock(); + final List args = callNode.getArgs(); + final Expression function = callNode.getFunction(); + final Block currentBlock = lc.getCurrentBlock(); final CodeGeneratorLexicalContext codegenLexicalContext = lc; + final Type callNodeType = callNode.getType(); function.accept(new NodeVisitor(new LexicalContext()) { @@ -593,7 +605,7 @@ final class CodeGenerator extends NodeOperatorVisitor(init) { + new Store(init) { @Override protected void storeNonDiscard() { return; @@ -877,9 +892,15 @@ final class CodeGenerator extends NodeOperatorVisitor= 0, like varargs. */ - if (function.needsParentScope()) { - initParentScope(); + if(method.hasScope()) { + if (function.needsParentScope()) { + method.loadCompilerConstant(CALLEE); + method.invoke(ScriptFunction.GET_SCOPE); + } else { + assert function.hasScopeBlock(); + method.loadNull(); + } + method.storeCompilerConstant(SCOPE); } if (function.needsArguments()) { initArguments(function); @@ -940,22 +961,12 @@ final class CodeGenerator extends NodeOperatorVisitor foc = new FieldObjectCreator(this, nameList, newSymbols, values, true, hasArguments) { + new FieldObjectCreator(this, nameList, newSymbols, values, true, hasArguments) { @Override protected void loadValue(final Symbol value) { method.load(value); } - - @Override - protected void loadScope(MethodEmitter m) { - if (function.needsParentScope()) { - m.loadCompilerConstant(SCOPE); - } else { - m.loadNull(); - } - } - }; - foc.makeObject(method); + }.makeObject(method); // runScript(): merge scope into global if (isFunctionBody && function.isProgram()) { @@ -995,12 +1006,6 @@ final class CodeGenerator extends NodeOperatorVisitor type = arrayType.getTypeClass(); @@ -1172,11 +1192,11 @@ final class CodeGenerator extends NodeOperatorVisitor args) { + private MethodEmitter loadArgsArray(final List args) { final Object[] array = new Object[args.size()]; loadConstant(array); @@ -1318,9 +1338,8 @@ final class CodeGenerator extends NodeOperatorVisitor literalNode) { assert literalNode.getSymbol() != null : literalNode + " has no symbol"; load(literalNode).store(literalNode.getSymbol()); return false; @@ -1330,16 +1349,16 @@ final class CodeGenerator extends NodeOperatorVisitor elements = objectNode.getElements(); - final List keys = new ArrayList<>(); - final List symbols = new ArrayList<>(); - final List values = new ArrayList<>(); + final List keys = new ArrayList<>(); + final List symbols = new ArrayList<>(); + final List values = new ArrayList<>(); boolean hasGettersSetters = false; for (PropertyNode propertyNode: elements) { - final Node value = propertyNode.getValue(); + final Expression value = propertyNode.getValue(); final String key = propertyNode.getKeyName(); - final Symbol symbol = value == null ? null : propertyNode.getSymbol(); + final Symbol symbol = value == null ? null : propertyNode.getKey().getSymbol(); if (value == null) { hasGettersSetters = true; @@ -1350,73 +1369,71 @@ final class CodeGenerator extends NodeOperatorVisitor(this, keys, symbols, values) { - @Override - protected void loadValue(final Node node) { - load(node); - } + if (elements.size() > OBJECT_SPILL_THRESHOLD) { + new SpillObjectCreator(this, keys, symbols, values).makeObject(method); + } else { + new FieldObjectCreator(this, keys, symbols, values) { + @Override + protected void loadValue(final Expression node) { + load(node); + } - /** - * Ensure that the properties start out as object types so that - * we can do putfield initializations instead of dynamicSetIndex - * which would be the case to determine initial property type - * otherwise. - * - * Use case, it's very expensive to do a million var x = {a:obj, b:obj} - * just to have to invalidate them immediately on initialization - * - * see NASHORN-594 - */ - @Override - protected MapCreator newMapCreator(final Class fieldObjectClass) { - return new MapCreator(fieldObjectClass, keys, symbols) { - @Override - protected int getPropertyFlags(final Symbol symbol, final boolean isVarArg) { - return super.getPropertyFlags(symbol, isVarArg) | Property.IS_ALWAYS_OBJECT; - } - }; - } + /** + * Ensure that the properties start out as object types so that + * we can do putfield initializations instead of dynamicSetIndex + * which would be the case to determine initial property type + * otherwise. + * + * Use case, it's very expensive to do a million var x = {a:obj, b:obj} + * just to have to invalidate them immediately on initialization + * + * see NASHORN-594 + */ + @Override + protected MapCreator newMapCreator(final Class fieldObjectClass) { + return new MapCreator(fieldObjectClass, keys, symbols) { + @Override + protected int getPropertyFlags(final Symbol symbol, final boolean hasArguments) { + return super.getPropertyFlags(symbol, hasArguments) | Property.IS_ALWAYS_OBJECT; + } + }; + } - }.makeObject(method); + }.makeObject(method); + } method.dup(); globalObjectPrototype(); method.invoke(ScriptObject.SET_PROTO); - if (!hasGettersSetters) { - method.store(objectNode.getSymbol()); - return false; - } + if (hasGettersSetters) { + for (final PropertyNode propertyNode : elements) { + final FunctionNode getter = propertyNode.getGetter(); + final FunctionNode setter = propertyNode.getSetter(); - for (final Node element : elements) { - final PropertyNode propertyNode = (PropertyNode)element; - final Object key = propertyNode.getKey(); - final FunctionNode getter = propertyNode.getGetter(); - final FunctionNode setter = propertyNode.getSetter(); + if (getter == null && setter == null) { + continue; + } - if (getter == null && setter == null) { - continue; + method.dup().loadKey(propertyNode.getKey()); + + if (getter == null) { + method.loadNull(); + } else { + getter.accept(this); + } + + if (setter == null) { + method.loadNull(); + } else { + setter.accept(this); + } + + method.invoke(ScriptObject.SET_USER_ACCESSORS); } - - method.dup().loadKey(key); - - if (getter == null) { - method.loadNull(); - } else { - getter.accept(this); - } - - if (setter == null) { - method.loadNull(); - } else { - setter.accept(this); - } - - method.invoke(ScriptObject.SET_USER_ACCESSORS); } method.store(objectNode.getSymbol()); - return false; } @@ -1428,7 +1445,7 @@ final class CodeGenerator extends NodeOperatorVisitor && ((LiteralNode) node).isNull(); } - private boolean nullCheck(final RuntimeNode runtimeNode, final List args, final String signature) { + private boolean nullCheck(final RuntimeNode runtimeNode, final List args, final String signature) { final Request request = runtimeNode.getRequest(); if (!Request.isEQ(request) && !Request.isNE(request)) { @@ -1453,11 +1470,11 @@ final class CodeGenerator extends NodeOperatorVisitor args) { + private boolean specializationCheck(final RuntimeNode.Request request, final Expression node, final List args) { if (!request.canSpecialize()) { return false; } @@ -1564,10 +1581,11 @@ final class CodeGenerator extends NodeOperatorVisitor args = runtimeNode.getArgs(); if (runtimeNode.isPrimitive() && !runtimeNode.isFinal() && isReducible(runtimeNode.getRequest())) { - final Node lhs = runtimeNode.getArgs().get(0); - assert runtimeNode.getArgs().size() > 1 : runtimeNode + " must have two args"; - final Node rhs = runtimeNode.getArgs().get(1); + final Expression lhs = args.get(0); + assert args.size() > 1 : runtimeNode + " must have two args"; + final Expression rhs = args.get(1); final Type type = runtimeNode.getType(); final Symbol symbol = runtimeNode.getSymbol(); @@ -1604,9 +1622,6 @@ final class CodeGenerator extends NodeOperatorVisitor args = runtimeNode.getArgs(); - if (nullCheck(runtimeNode, args, new FunctionSignature(false, false, runtimeNode.getType(), args).toString())) { return false; } @@ -1615,7 +1630,7 @@ final class CodeGenerator extends NodeOperatorVisitor cases = switchNode.getCases(); @@ -1847,7 +1860,7 @@ final class CodeGenerator extends NodeOperatorVisitor exprClass = type.getTypeClass(); method.invoke(staticCallNoLookup(ScriptRuntime.class, "switchTagAsInt", int.class, exprClass.isPrimitive()? exprClass : Object.class, int.class)); } @@ -1878,11 +1891,13 @@ final class CodeGenerator extends NodeOperatorVisitor(exception) { @Override @@ -2045,7 +2060,7 @@ final class CodeGenerator extends NodeOperatorVisitor args = callNode.getArgs(); + final List args = callNode.getArgs(); // Load function reference. load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error @@ -2309,7 +2322,7 @@ final class CodeGenerator extends NodeOperatorVisitor */ - private abstract class SelfModifyingStore extends Store { - protected SelfModifyingStore(final T assignNode, final Node target) { + private abstract class SelfModifyingStore extends Store { + protected SelfModifyingStore(final T assignNode, final Expression target) { super(assignNode, target); } @@ -2938,13 +2958,13 @@ final class CodeGenerator extends NodeOperatorVisitor { + private abstract class Store { /** An assignment node, e.g. x += y */ protected final T assignNode; /** The target node to store to, e.g. x */ - private final Node target; + private final Expression target; /** How deep on the stack do the arguments go if this generates an indy call */ private int depth; @@ -2958,7 +2978,7 @@ final class CodeGenerator extends NodeOperatorVisitor(), new ArrayList(), false, false) { - @Override - protected void makeObject(final MethodEmitter m) { - final String className = SCRIPTFUNCTION_IMPL_OBJECT; + method._new(className).dup(); + loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), allocatorClassName, allocatorMap)); - m._new(className).dup(); - loadConstant(new RecompilableScriptFunctionData(functionNode, compiler.getCodeInstaller(), Compiler.binaryName(getClassName()), makeMap())); - - if (isLazy || functionNode.needsParentScope()) { - m.loadCompilerConstant(SCOPE); - } else { - m.loadNull(); - } - m.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class)); - } - }.makeObject(method); + if (functionNode.isLazy() || functionNode.needsParentScope()) { + method.loadCompilerConstant(SCOPE); + } else { + method.loadNull(); + } + method.invoke(constructorNoLookup(className, RecompilableScriptFunctionData.class, ScriptObject.class)); } - /* - * Globals are special. We cannot refer to any Global (or NativeObject) class by .class, as they are different - * for different contexts. As far as I can tell, the only NativeObject that we need to deal with like this - * is from the code pipeline is Global - */ + // calls on Global class. private MethodEmitter globalInstance() { return method.invokestatic(GLOBAL_OBJECT, "instance", "()L" + GLOBAL_OBJECT + ';'); } diff --git a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java index 9f263067c80..5d48fb7358d 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilationPhase.java @@ -18,17 +18,16 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; - import jdk.nashorn.internal.codegen.types.Range; import jdk.nashorn.internal.codegen.types.Type; -import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.LexicalContext; +import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.Symbol; -import jdk.nashorn.internal.ir.FunctionNode.CompilationState; -import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.TemporarySymbols; import jdk.nashorn.internal.ir.debug.ASTWriter; import jdk.nashorn.internal.ir.debug.PrintVisitor; @@ -117,7 +116,7 @@ enum CompilationPhase { final FunctionNode parent = lc.getParentFunction(functionNode); assert parent != null; lc.setFlag(parent, FunctionNode.HAS_LAZY_CHILDREN); - lc.setFlag(parent.getBody(), Block.NEEDS_SCOPE); + lc.setBlockNeedsScope(parent.getBody()); lc.setFlag(functionNode, FunctionNode.IS_LAZY); return functionNode; } @@ -258,17 +257,20 @@ enum CompilationPhase { @Override public Node leaveDefault(final Node node) { - final Symbol symbol = node.getSymbol(); - if (symbol != null) { - final Range range = symbol.getRange(); - final Type symbolType = symbol.getSymbolType(); - if (!symbolType.isNumeric()) { - return node; - } - final Type rangeType = range.getType(); - if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range - RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange()); - return node.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols())); + if(node instanceof Expression) { + final Expression expr = (Expression)node; + final Symbol symbol = expr.getSymbol(); + if (symbol != null) { + final Range range = symbol.getRange(); + final Type symbolType = symbol.getSymbolType(); + if (!symbolType.isNumeric()) { + return expr; + } + final Type rangeType = range.getType(); + if (!Type.areEquivalent(symbolType, rangeType) && Type.widest(symbolType, rangeType) == symbolType) { //we can narrow range + RangeAnalyzer.LOG.info("[", lc.getCurrentFunction().getName(), "] ", symbol, " can be ", range.getType(), " ", symbol.getRange()); + return expr.setSymbol(lc, symbol.setTypeOverrideShared(range.getType(), compiler.getTemporarySymbols())); + } } } return node; diff --git a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java index 72fcf178bea..a31358f6503 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/Compiler.java @@ -528,8 +528,8 @@ public final class Compiler { return this.env; } - private static String safeSourceName(final Source source) { - String baseName = new File(source.getName()).getName(); + private String safeSourceName(final Source src) { + String baseName = new File(src.getName()).getName(); final int index = baseName.lastIndexOf(".js"); if (index != -1) { @@ -537,6 +537,9 @@ public final class Compiler { } baseName = baseName.replace('.', '_').replace('-', '_'); + if (! env._loader_per_compile) { + baseName = baseName + installer.getUniqueScriptId(); + } final String mangled = NameCodec.encode(baseName); return mangled != null ? mangled : baseName; diff --git a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java index 3deff98c00a..c8ccc1cedc1 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java @@ -100,10 +100,10 @@ public enum CompilerConstants { CALLEE(":callee", ScriptFunction.class), /** the varargs variable when necessary */ - VARARGS(":varargs"), + VARARGS(":varargs", Object[].class), /** the arguments vector when necessary and the slot */ - ARGUMENTS("arguments", Object.class, 2), + ARGUMENTS("arguments", ScriptObject.class, 2), /** prefix for iterators for for (x in ...) */ ITERATOR_PREFIX(":i", Iterator.class), diff --git a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java index 974b5ba81a7..9e15b4978a4 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java @@ -26,15 +26,16 @@ package jdk.nashorn.internal.codegen; import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; -import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; +import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount; import static jdk.nashorn.internal.codegen.types.Type.OBJECT; import java.util.Iterator; import java.util.List; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.arrays.ArrayIndex; @@ -48,6 +49,13 @@ import jdk.nashorn.internal.runtime.arrays.ArrayIndex; * @see jdk.nashorn.internal.ir.Node */ public abstract class FieldObjectCreator extends ObjectCreator { + + private String fieldObjectClassName; + private Class fieldObjectClass; + private int fieldCount; + private int paddedFieldCount; + private int paramCount; + /** array of corresponding values to symbols (null for no values) */ private final List values; @@ -80,14 +88,9 @@ public abstract class FieldObjectCreator extends ObjectCreator { super(codegen, keys, symbols, isScope, hasArguments); this.values = values; this.callSiteFlags = codegen.getCallSiteFlags(); - } - /** - * Loads the scope on the stack through the passed method emitter. - * @param method the method emitter to use - */ - protected void loadScope(final MethodEmitter method) { - method.loadCompilerConstant(SCOPE); + countFields(); + findClass(); } /** @@ -137,6 +140,13 @@ public abstract class FieldObjectCreator extends ObjectCreator { } } + @Override + protected PropertyMap makeMap() { + assert propertyMap == null : "property map already initialized"; + propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount); + return propertyMap; + } + /** * Technique for loading an initial value. Defined by anonymous subclasses in code gen. * @@ -173,4 +183,47 @@ public abstract class FieldObjectCreator extends ObjectCreator { loadValue(value); method.dynamicSetIndex(callSiteFlags); } + + /** + * Locate (or indirectly create) the object container class. + */ + private void findClass() { + fieldObjectClassName = isScope() ? + ObjectClassGenerator.getClassName(fieldCount, paramCount) : + ObjectClassGenerator.getClassName(paddedFieldCount); + + try { + this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName)); + } catch (final ClassNotFoundException e) { + throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created."); + } + } + + /** + * Get the class name for the object class, + * e.g. {@code com.nashorn.oracle.scripts.JO2P0} + * + * @return script class name + */ + String getClassName() { + return fieldObjectClassName; + } + + /** + * Tally the number of fields and parameters. + */ + private void countFields() { + for (final Symbol symbol : this.symbols) { + if (symbol != null) { + if (hasArguments() && symbol.isParam()) { + symbol.setFieldIndex(paramCount++); + } else { + symbol.setFieldIndex(fieldCount++); + } + } + } + + paddedFieldCount = getPaddedFieldCount(fieldCount); + } + } diff --git a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java index f9d643228e8..476ac745619 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/FinalizeTypes.java @@ -31,7 +31,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; import java.util.ArrayList; import java.util.HashSet; import java.util.List; - import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.Assignment; @@ -40,7 +39,8 @@ import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; -import jdk.nashorn.internal.ir.ExecuteNode; +import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; @@ -147,9 +147,9 @@ final class FinalizeTypes extends NodeOperatorVisitor { * strings etc as well. */ @Override - public Node leaveADD(final BinaryNode binaryNode) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + public Expression leaveADD(final BinaryNode binaryNode) { + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); final Type type = binaryNode.getType(); @@ -175,6 +175,14 @@ final class FinalizeTypes extends NodeOperatorVisitor { if (destType == null) { destType = specBinaryNode.getType(); } + // Register assignments to this object in case this is used as constructor + if (binaryNode.lhs() instanceof AccessNode) { + AccessNode accessNode = (AccessNode) binaryNode.lhs(); + + if (accessNode.getBase().getSymbol().isThis()) { + lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName()); + } + } return specBinaryNode.setRHS(convert(specBinaryNode.rhs(), destType)); } @@ -233,7 +241,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { return leaveASSIGN(binaryNode); } - private boolean symbolIsInteger(Node node) { + private boolean symbolIsInteger(final Expression node) { final Symbol symbol = node.getSymbol(); assert symbol != null && symbol.getSymbolType().isInteger() : "int coercion expected: " + Debug.id(symbol) + " " + symbol + " " + lc.getCurrentFunction().getSource(); return true; @@ -365,7 +373,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveCatchNode(final CatchNode catchNode) { - final Node exceptionCondition = catchNode.getExceptionCondition(); + final Expression exceptionCondition = catchNode.getExceptionCondition(); if (exceptionCondition != null) { return catchNode.setExceptionCondition(convert(exceptionCondition, Type.BOOLEAN)); } @@ -373,16 +381,16 @@ final class FinalizeTypes extends NodeOperatorVisitor { } @Override - public Node leaveExecuteNode(final ExecuteNode executeNode) { + public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { temporarySymbols.reuse(); - return executeNode.setExpression(discard(executeNode.getExpression())); + return expressionStatement.setExpression(discard(expressionStatement.getExpression())); } @Override public Node leaveForNode(final ForNode forNode) { - final Node init = forNode.getInit(); - final Node test = forNode.getTest(); - final Node modify = forNode.getModify(); + final Expression init = forNode.getInit(); + final Expression test = forNode.getTest(); + final Expression modify = forNode.getModify(); if (forNode.isForIn()) { return forNode.setModify(lc, convert(forNode.getModify(), Type.OBJECT)); // NASHORN-400 @@ -407,10 +415,10 @@ final class FinalizeTypes extends NodeOperatorVisitor { if (!functionNode.needsCallee()) { functionNode.compilerConstant(CALLEE).setNeedsSlot(false); } - // Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope or its - // own scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope earlier than - // this phase. - if (!(functionNode.getBody().needsScope() || functionNode.needsParentScope())) { + // Similar reasoning applies to __scope__ symbol: if the function doesn't need either parent scope and none of + // its blocks create a scope, we ensure it doesn't get a slot, but we can't determine whether it needs a scope + // earlier than this phase. + if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) { functionNode.compilerConstant(SCOPE).setNeedsSlot(false); } @@ -432,13 +440,13 @@ final class FinalizeTypes extends NodeOperatorVisitor { public boolean enterLiteralNode(final LiteralNode literalNode) { if (literalNode instanceof ArrayLiteralNode) { final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode; - final Node[] array = arrayLiteralNode.getValue(); + final Expression[] array = arrayLiteralNode.getValue(); final Type elementType = arrayLiteralNode.getElementType(); for (int i = 0; i < array.length; i++) { final Node element = array[i]; if (element != null) { - array[i] = convert(element.accept(this), elementType); + array[i] = convert((Expression)element.accept(this), elementType); } } } @@ -448,7 +456,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveReturnNode(final ReturnNode returnNode) { - final Node expr = returnNode.getExpression(); + final Expression expr = returnNode.getExpression(); if (expr != null) { return returnNode.setExpression(convert(expr, lc.getCurrentFunction().getReturnType())); } @@ -457,8 +465,8 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveRuntimeNode(final RuntimeNode runtimeNode) { - final List args = runtimeNode.getArgs(); - for (final Node arg : args) { + final List args = runtimeNode.getArgs(); + for (final Expression arg : args) { assert !arg.getType().isUnknown(); } return runtimeNode; @@ -472,12 +480,12 @@ final class FinalizeTypes extends NodeOperatorVisitor { return switchNode; } - final Node expression = switchNode.getExpression(); + final Expression expression = switchNode.getExpression(); final List cases = switchNode.getCases(); final List newCases = new ArrayList<>(); for (final CaseNode caseNode : cases) { - final Node test = caseNode.getTest(); + final Expression test = caseNode.getTest(); newCases.add(test != null ? caseNode.setTest(convert(test, Type.OBJECT)) : caseNode); } @@ -488,7 +496,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveTernaryNode(final TernaryNode ternaryNode) { - return ternaryNode.setLHS(convert(ternaryNode.lhs(), Type.BOOLEAN)); + return ternaryNode.setTest(convert(ternaryNode.getTest(), Type.BOOLEAN)); } @Override @@ -498,16 +506,16 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveVarNode(final VarNode varNode) { - final Node init = varNode.getInit(); + final Expression init = varNode.getInit(); if (init != null) { final SpecializedNode specialized = specialize(varNode); final VarNode specVarNode = (VarNode)specialized.node; Type destType = specialized.type; if (destType == null) { - destType = specVarNode.getType(); + destType = specVarNode.getName().getType(); } - assert specVarNode.hasType() : specVarNode + " doesn't have a type"; - final Node convertedInit = convert(init, destType); + assert specVarNode.getName().hasType() : specVarNode + " doesn't have a type"; + final Expression convertedInit = convert(init, destType); temporarySymbols.reuse(); return specVarNode.setInit(convertedInit); } @@ -517,7 +525,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { @Override public Node leaveWhileNode(final WhileNode whileNode) { - final Node test = whileNode.getTest(); + final Expression test = whileNode.getTest(); if (test != null) { return whileNode.setTest(lc, convert(test, Type.BOOLEAN)); } @@ -592,8 +600,8 @@ final class FinalizeTypes extends NodeOperatorVisitor { */ @SuppressWarnings("fallthrough") private Node leaveCmp(final BinaryNode binaryNode, final RuntimeNode.Request request) { - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); Type widest = Type.widest(lhs.getType(), rhs.getType()); @@ -689,10 +697,10 @@ final class FinalizeTypes extends NodeOperatorVisitor { } } - SpecializedNode specialize(final Assignment assignment) { + SpecializedNode specialize(final Assignment assignment) { final Node node = ((Node)assignment); final T lhs = assignment.getAssignmentDest(); - final Node rhs = assignment.getAssignmentSource(); + final Expression rhs = assignment.getAssignmentSource(); if (!canHaveCallSiteType(lhs)) { return new SpecializedNode(node, null); @@ -711,8 +719,16 @@ final class FinalizeTypes extends NodeOperatorVisitor { } final Node newNode = assignment.setAssignmentDest(setTypeOverride(lhs, to)); - final Node typePropagatedNode = propagateType(newNode, to); - + final Node typePropagatedNode; + if(newNode instanceof Expression) { + typePropagatedNode = propagateType((Expression)newNode, to); + } else if(newNode instanceof VarNode) { + // VarNode, being a statement, doesn't have its own symbol; it uses the symbol of its name instead. + final VarNode varNode = (VarNode)newNode; + typePropagatedNode = varNode.setName((IdentNode)propagateType(varNode.getName(), to)); + } else { + throw new AssertionError(); + } return new SpecializedNode(typePropagatedNode, to); } @@ -752,7 +768,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { * @param to new type */ @SuppressWarnings("unchecked") - T setTypeOverride(final T node, final Type to) { + T setTypeOverride(final T node, final Type to) { final Type from = node.getType(); if (!node.getType().equals(to)) { LOG.info("Changing call override type for '", node, "' from ", node.getType(), " to ", to); @@ -781,7 +797,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { * @param to destination type * @return conversion node */ - private Node convert(final Node node, final Type to) { + private Expression convert(final Expression node, final Type to) { assert !to.isUnknown() : "unknown type for " + node + " class=" + node.getClass(); assert node != null : "node is null"; assert node.getSymbol() != null : "node " + node + " " + node.getClass() + " has no symbol! " + lc.getCurrentFunction(); @@ -797,7 +813,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { return node; } - Node resultNode = node; + Expression resultNode = node; if (node instanceof LiteralNode && !(node instanceof ArrayLiteralNode) && !to.isObject()) { final LiteralNode newNode = new LiteralNodeConstantEvaluator((LiteralNode)node, to).eval(); @@ -821,9 +837,9 @@ final class FinalizeTypes extends NodeOperatorVisitor { return temporarySymbols.ensureSymbol(lc, to, resultNode); } - private static Node discard(final Node node) { + private static Expression discard(final Expression node) { if (node.getSymbol() != null) { - final Node discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node); + final UnaryNode discard = new UnaryNode(Token.recast(node.getToken(), TokenType.DISCARD), node); //discard never has a symbol in the discard node - then it would be a nop assert !node.isTerminal(); return discard; @@ -846,7 +862,7 @@ final class FinalizeTypes extends NodeOperatorVisitor { * @param node * @param to */ - private Node propagateType(final Node node, final Type to) { + private Expression propagateType(final Expression node, final Type to) { Symbol symbol = node.getSymbol(); if (symbol.isTemp() && symbol.getSymbolType() != to) { symbol = symbol.setTypeOverrideShared(to, temporarySymbols); diff --git a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java index 2331cf7570d..49dfbb324b7 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/FoldConstants.java @@ -28,8 +28,8 @@ package jdk.nashorn.internal.codegen; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; +import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.EmptyNode; -import jdk.nashorn.internal.ir.ExecuteNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; import jdk.nashorn.internal.ir.IfNode; @@ -91,7 +91,7 @@ final class FoldConstants extends NodeVisitor { if (test instanceof LiteralNode) { final Block shortCut = ((LiteralNode)test).isTrue() ? ifNode.getPass() : ifNode.getFail(); if (shortCut != null) { - return new ExecuteNode(shortCut.getLineNumber(), shortCut.getToken(), shortCut.getFinish(), shortCut); + return new BlockStatement(ifNode.getLineNumber(), shortCut); } return new EmptyNode(ifNode); } @@ -100,9 +100,9 @@ final class FoldConstants extends NodeVisitor { @Override public Node leaveTernaryNode(final TernaryNode ternaryNode) { - final Node test = ternaryNode.lhs(); + final Node test = ternaryNode.getTest(); if (test instanceof LiteralNode) { - return ((LiteralNode)test).isTrue() ? ternaryNode.rhs() : ternaryNode.third(); + return ((LiteralNode)test).isTrue() ? ternaryNode.getTrueExpression() : ternaryNode.getFalseExpression(); } return ternaryNode; } diff --git a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java index 057d2d4e454..1c1b5f7469f 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/FunctionSignature.java @@ -31,8 +31,8 @@ import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.List; import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; -import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.linker.LinkerCallSite; @@ -63,7 +63,7 @@ public final class FunctionSignature { * @param retType what is the return type * @param args argument list of AST Nodes */ - public FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final List args) { + public FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final List args) { this(hasSelf, hasCallee, retType, FunctionSignature.typeArray(args)); } @@ -167,7 +167,7 @@ public final class FunctionSignature { * * @return the array of types */ - private static Type[] typeArray(final List args) { + private static Type[] typeArray(final List args) { if (args == null) { return null; } @@ -175,7 +175,7 @@ public final class FunctionSignature { final Type[] typeArray = new Type[args.size()]; int pos = 0; - for (final Node arg : args) { + for (final Expression arg : args) { typeArray[pos++] = arg.getType(); } diff --git a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java index 013564c2760..cc109695d38 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/Lower.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/Lower.java @@ -32,16 +32,19 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.THIS; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.ListIterator; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BlockLexicalContext; +import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.EmptyNode; -import jdk.nashorn.internal.ir.ExecuteNode; +import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; @@ -115,6 +118,21 @@ final class Lower extends NodeOperatorVisitor { } return newStatements; } + + @Override + protected Block afterSetStatements(final Block block) { + final List stmts = block.getStatements(); + for(final ListIterator li = stmts.listIterator(stmts.size()); li.hasPrevious();) { + final Statement stmt = li.previous(); + // popStatements() guarantees that the only thing after a terminal statement are uninitialized + // VarNodes. We skip past those, and set the terminal state of the block to the value of the + // terminal state of the first statement that is not an uninitialized VarNode. + if(!(stmt instanceof VarNode && ((VarNode)stmt).getInit() == null)) { + return block.setIsTerminal(this, stmt.isTerminal()); + } + } + return block.setIsTerminal(this, false); + } }); } @@ -122,7 +140,7 @@ final class Lower extends NodeOperatorVisitor { public boolean enterBlock(final Block block) { final FunctionNode function = lc.getCurrentFunction(); if (lc.isFunctionBody() && function.isProgram() && !function.hasDeclaredFunctions()) { - new ExecuteNode(block.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this); + new ExpressionStatement(function.getLineNumber(), block.getToken(), block.getFinish(), LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)).accept(this); } return true; } @@ -132,24 +150,20 @@ final class Lower extends NodeOperatorVisitor { //now we have committed the entire statement list to the block, but we need to truncate //whatever is after the last terminal. block append won't append past it - Statement last = lc.getLastStatement(); if (lc.isFunctionBody()) { final FunctionNode currentFunction = lc.getCurrentFunction(); final boolean isProgram = currentFunction.isProgram(); + final Statement last = lc.getLastStatement(); final ReturnNode returnNode = new ReturnNode( - last == null ? block.getLineNumber() : last.getLineNumber(), //TODO? + last == null ? currentFunction.getLineNumber() : last.getLineNumber(), //TODO? currentFunction.getToken(), currentFunction.getFinish(), isProgram ? compilerConstant(RETURN) : LiteralNode.newInstance(block, ScriptRuntime.UNDEFINED)); - last = (Statement)returnNode.accept(this); - } - - if (last != null && last.isTerminal()) { - return block.setIsTerminal(lc, true); + returnNode.accept(this); } return block; @@ -183,29 +197,32 @@ final class Lower extends NodeOperatorVisitor { } @Override - public Node leaveExecuteNode(final ExecuteNode executeNode) { - final Node expr = executeNode.getExpression(); - ExecuteNode node = executeNode; + public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { + final Expression expr = expressionStatement.getExpression(); + ExpressionStatement node = expressionStatement; final FunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction.isProgram()) { - if (!(expr instanceof Block) || expr instanceof FunctionNode) { // it's not a block, but can be a function - if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { - node = executeNode.setExpression( - new BinaryNode( - Token.recast( - executeNode.getToken(), - TokenType.ASSIGN), - compilerConstant(RETURN), - expr)); - } + if (!isInternalExpression(expr) && !isEvalResultAssignment(expr)) { + node = expressionStatement.setExpression( + new BinaryNode( + Token.recast( + expressionStatement.getToken(), + TokenType.ASSIGN), + compilerConstant(RETURN), + expr)); } } return addStatement(node); } + @Override + public Node leaveBlockStatement(BlockStatement blockStatement) { + return addStatement(blockStatement); + } + @Override public Node leaveForNode(final ForNode forNode) { ForNode newForNode = forNode; @@ -290,11 +307,11 @@ final class Lower extends NodeOperatorVisitor { final IdentNode exception = new IdentNode(token, finish, lc.getCurrentFunction().uniqueName("catch_all")); - final Block catchBody = new Block(lineNumber, token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW)). + final Block catchBody = new Block(token, finish, new ThrowNode(lineNumber, token, finish, new IdentNode(exception), ThrowNode.IS_SYNTHETIC_RETHROW)). setIsTerminal(lc, true); //ends with throw, so terminal final CatchNode catchAllNode = new CatchNode(lineNumber, token, finish, new IdentNode(exception), null, catchBody, CatchNode.IS_SYNTHETIC_RETHROW); - final Block catchAllBlock = new Block(lineNumber, token, finish, catchAllNode); + final Block catchAllBlock = new Block(token, finish, catchAllNode); //catchallblock -> catchallnode (catchnode) -> exception -> throw @@ -343,14 +360,14 @@ final class Lower extends NodeOperatorVisitor { if (!isTerminal(newStatements)) { newStatements.add(throwNode); } - return new Block(throwNode.getLineNumber(), throwNode.getToken(), throwNode.getFinish(), newStatements); + return BlockStatement.createReplacement(throwNode, newStatements); } return throwNode; } @Override public Node leaveBreakNode(final BreakNode breakNode) { - return copy(breakNode, Lower.this.lc.getBreakable(breakNode.getLabel())); + return copy(breakNode, (Node)Lower.this.lc.getBreakable(breakNode.getLabel())); } @Override @@ -360,15 +377,15 @@ final class Lower extends NodeOperatorVisitor { @Override public Node leaveReturnNode(final ReturnNode returnNode) { - final Node expr = returnNode.getExpression(); + final Expression expr = returnNode.getExpression(); final List newStatements = new ArrayList<>(); - final Node resultNode; + final Expression resultNode; if (expr != null) { //we need to evaluate the result of the return in case it is complex while //still in the try block, store it in a result value and return it afterwards resultNode = new IdentNode(Lower.this.compilerConstant(RETURN)); - newStatements.add(new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); + newStatements.add(new ExpressionStatement(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new BinaryNode(Token.recast(returnNode.getToken(), TokenType.ASSIGN), resultNode, expr))); } else { resultNode = null; } @@ -378,7 +395,7 @@ final class Lower extends NodeOperatorVisitor { newStatements.add(expr == null ? returnNode : returnNode.setExpression(resultNode)); } - return new ExecuteNode(returnNode.getLineNumber(), returnNode.getToken(), returnNode.getFinish(), new Block(returnNode.getLineNumber(), returnNode.getToken(), lc.getCurrentBlock().getFinish(), newStatements)); + return BlockStatement.createReplacement(returnNode, lc.getCurrentBlock().getFinish(), newStatements); } private Node copy(final Statement endpoint, final Node targetNode) { @@ -387,7 +404,7 @@ final class Lower extends NodeOperatorVisitor { if (!isTerminal(newStatements)) { newStatements.add(endpoint); } - return new ExecuteNode(endpoint.getLineNumber(), endpoint.getToken(), endpoint.getFinish(), new Block(endpoint.getLineNumber(), endpoint.getToken(), finish, newStatements)); + return BlockStatement.createReplacement(endpoint, finish, newStatements); } return endpoint; } @@ -449,7 +466,7 @@ final class Lower extends NodeOperatorVisitor { if (tryNode.getCatchBlocks().isEmpty()) { newTryNode = tryNode.setFinallyBody(null); } else { - Block outerBody = new Block(tryNode.getLineNumber(), tryNode.getToken(), tryNode.getFinish(), new ArrayList(Arrays.asList(tryNode.setFinallyBody(null)))); + Block outerBody = new Block(tryNode.getToken(), tryNode.getFinish(), new ArrayList(Arrays.asList(tryNode.setFinallyBody(null)))); newTryNode = tryNode.setBody(outerBody).setCatchBlocks(null); } @@ -466,7 +483,7 @@ final class Lower extends NodeOperatorVisitor { public Node leaveVarNode(final VarNode varNode) { addStatement(varNode); if (varNode.getFlag(VarNode.IS_LAST_FUNCTION_DECLARATION) && lc.getCurrentFunction().isProgram()) { - new ExecuteNode(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this); + new ExpressionStatement(varNode.getLineNumber(), varNode.getToken(), varNode.getFinish(), new IdentNode(varNode.getName())).accept(this); } return varNode; } @@ -499,7 +516,7 @@ final class Lower extends NodeOperatorVisitor { * @param function function called by a CallNode * @return transformed node to marker function or identity if not ident/access/indexnode */ - private static Node markerFunction(final Node function) { + private static Expression markerFunction(final Expression function) { if (function instanceof IdentNode) { return ((IdentNode)function).setIsFunction(); } else if (function instanceof BaseNode) { @@ -541,15 +558,15 @@ final class Lower extends NodeOperatorVisitor { private CallNode checkEval(final CallNode callNode) { if (callNode.getFunction() instanceof IdentNode) { - final List args = callNode.getArgs(); - final IdentNode callee = (IdentNode)callNode.getFunction(); + final List args = callNode.getArgs(); + final IdentNode callee = (IdentNode)callNode.getFunction(); // 'eval' call with at least one argument if (args.size() >= 1 && EVAL.symbolName().equals(callee.getName())) { final FunctionNode currentFunction = lc.getCurrentFunction(); return callNode.setEvalArgs( new CallNode.EvalArgs( - ensureUniqueNamesIn(args.get(0)).accept(this), + (Expression)ensureUniqueNamesIn(args.get(0)).accept(this), compilerConstant(THIS), evalLocation(callee), currentFunction.isStrict())); @@ -618,7 +635,7 @@ final class Lower extends NodeOperatorVisitor { * @param expression expression to check for internal symbol * @return true if internal, false otherwise */ - private static boolean isInternalExpression(final Node expression) { + private static boolean isInternalExpression(final Expression expression) { final Symbol symbol = expression.getSymbol(); return symbol != null && symbol.isInternal(); } diff --git a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java index 609fac9b3ff..6ad03c73691 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java @@ -41,10 +41,10 @@ public class MapCreator { private final Class structure; /** key set for object map */ - private final String[] keys; + final List keys; /** corresponding symbol set for object map */ - private final Symbol[] symbols; + final List symbols; /** * Constructor @@ -54,11 +54,9 @@ public class MapCreator { * @param symbols list of symbols for map */ MapCreator(final Class structure, final List keys, final List symbols) { - final int size = keys.size(); - this.structure = structure; - this.keys = keys.toArray(new String[size]); - this.symbols = symbols.toArray(new Symbol[size]); + this.keys = keys; + this.symbols = symbols; } /** @@ -70,21 +68,37 @@ public class MapCreator { * * @return New map populated with accessor properties. */ - PropertyMap makeMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) { + PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) { final List properties = new ArrayList<>(); - assert keys != null; - for (int i = 0; i < keys.length; i++) { - final String key = keys[i]; - final Symbol symbol = symbols[i]; + for (int i = 0, length = keys.size(); i < length; i++) { + final String key = keys.get(i); + final Symbol symbol = symbols.get(i); if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) { properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex())); } } - return PropertyMap.newMap(structure, properties, fieldCount, fieldMaximum); + return PropertyMap.newMap(properties, fieldCount, fieldMaximum, 0); + } + + PropertyMap makeSpillMap(final boolean hasArguments) { + final List properties = new ArrayList<>(); + int spillIndex = 0; + assert keys != null; + + for (int i = 0, length = keys.size(); i < length; i++) { + final String key = keys.get(i); + final Symbol symbol = symbols.get(i); + + if (symbol != null && !ArrayIndex.isIntArrayIndex(key)) { + properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++)); + } + } + + return PropertyMap.newMap(properties, 0, 0, spillIndex); } /** diff --git a/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java b/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java index 934df7a6820..fa3d19e5d05 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java @@ -73,11 +73,6 @@ public final class ObjectClassGenerator { */ static final int FIELD_PADDING = 4; - /** - * Rounding when calculating the number of fields. - */ - static final int FIELD_ROUNDING = 4; - /** * Debug field logger * Should we print debugging information for fields when they are generated and getters/setters are called? @@ -325,7 +320,6 @@ public final class ObjectClassGenerator { final List initFields = addFields(classEmitter, fieldCount); final MethodEmitter init = newInitMethod(classEmitter); - initializeToUndefined(init, className, initFields); init.returnVoid(); init.end(); @@ -441,13 +435,13 @@ public final class ObjectClassGenerator { * @return Open method emitter. */ private static MethodEmitter newInitScopeWithArgumentsMethod(final ClassEmitter classEmitter) { - final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, Object.class); + final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, ScriptObject.class); init.begin(); init.load(Type.OBJECT, JAVA_THIS.slot()); init.load(Type.OBJECT, INIT_MAP.slot()); init.load(Type.OBJECT, INIT_SCOPE.slot()); init.load(Type.OBJECT, INIT_ARGUMENTS.slot()); - init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, Object.class)); + init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, ScriptObject.class)); return init; } @@ -709,6 +703,15 @@ public final class ObjectClassGenerator { } } + /** + * Add padding to field count to avoid creating too many classes and have some spare fields + * @param count the field count + * @return the padded field count + */ + static int getPaddedFieldCount(final int count) { + return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING; + } + // // Provide generic getters and setters for undefined types. If a type is undefined, all // and marshals the set to the correct setter depending on the type of the value being set. diff --git a/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java b/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java index b0e5a7730e9..2a8ebba71b4 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java @@ -25,10 +25,10 @@ package jdk.nashorn.internal.codegen; +import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE; + import java.util.List; -import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_PADDING; import jdk.nashorn.internal.ir.Symbol; -import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.PropertyMap; /** @@ -36,9 +36,6 @@ import jdk.nashorn.internal.runtime.PropertyMap; */ public abstract class ObjectCreator { - /** Compile unit for this ObjectCreator, see CompileUnit */ - //protected final CompileUnit compileUnit; - /** List of keys to initiate in this ObjectCreator */ protected final List keys; @@ -50,12 +47,7 @@ public abstract class ObjectCreator { private final boolean isScope; private final boolean hasArguments; - private int fieldCount; - private int paddedFieldCount; - private int paramCount; - private String fieldObjectClassName; - private Class fieldObjectClass; - private PropertyMap propertyMap; + protected PropertyMap propertyMap; /** * Constructor @@ -72,41 +64,6 @@ public abstract class ObjectCreator { this.symbols = symbols; this.isScope = isScope; this.hasArguments = hasArguments; - - countFields(); - findClass(); - } - - /** - * Tally the number of fields and parameters. - */ - private void countFields() { - for (final Symbol symbol : this.symbols) { - if (symbol != null) { - if (hasArguments() && symbol.isParam()) { - symbol.setFieldIndex(paramCount++); - } else { - symbol.setFieldIndex(fieldCount++); - } - } - } - - paddedFieldCount = fieldCount + FIELD_PADDING; - } - - /** - * Locate (or indirectly create) the object container class. - */ - private void findClass() { - fieldObjectClassName = isScope() ? - ObjectClassGenerator.getClassName(fieldCount, paramCount) : - ObjectClassGenerator.getClassName(paddedFieldCount); - - try { - this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName)); - } catch (final ClassNotFoundException e) { - throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created."); - } } /** @@ -115,6 +72,12 @@ public abstract class ObjectCreator { */ protected abstract void makeObject(final MethodEmitter method); + /** + * Construct the property map appropriate for the object. + * @return the newly created property map + */ + protected abstract PropertyMap makeMap(); + /** * Create a new MapCreator * @param clazz type of MapCreator @@ -125,12 +88,11 @@ public abstract class ObjectCreator { } /** - * Construct the property map appropriate for the object. - * @return the newly created property map + * Loads the scope on the stack through the passed method emitter. + * @param method the method emitter to use */ - protected PropertyMap makeMap() { - propertyMap = newMapCreator(fieldObjectClass).makeMap(hasArguments(), fieldCount, paddedFieldCount); - return propertyMap; + protected void loadScope(final MethodEmitter method) { + method.loadCompilerConstant(SCOPE); } /** @@ -143,16 +105,6 @@ public abstract class ObjectCreator { return method; } - /** - * Get the class name for the object class, - * e.g. {@code com.nashorn.oracle.scripts.JO2P0} - * - * @return script class name - */ - String getClassName() { - return fieldObjectClassName; - } - /** * Is this a scope object * @return true if scope diff --git a/nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java b/nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java index ab61d374818..4056ec0c2bd 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/RangeAnalyzer.java @@ -28,11 +28,11 @@ package jdk.nashorn.internal.codegen; import java.util.HashMap; import java.util.HashSet; import java.util.Map; - import jdk.nashorn.internal.codegen.types.Range; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Assignment; import jdk.nashorn.internal.ir.BinaryNode; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.LexicalContext; @@ -87,7 +87,7 @@ final class RangeAnalyzer extends NodeOperatorVisitor { } //destination visited - private Symbol setRange(final Node dest, final Range range) { + private Symbol setRange(final Expression dest, final Range range) { if (range.isUnknown()) { return null; } @@ -352,7 +352,6 @@ final class RangeAnalyzer extends NodeOperatorVisitor { range = range.isUnknown() ? Range.createGenericRange() : range; setRange(node.getName(), range); - setRange(node, range); } return node; @@ -438,12 +437,12 @@ final class RangeAnalyzer extends NodeOperatorVisitor { * @return */ private static Symbol findLoopCounter(final LoopNode node) { - final Node test = node.getTest(); + final Expression test = node.getTest(); if (test != null && test.isComparison()) { final BinaryNode binaryNode = (BinaryNode)test; - final Node lhs = binaryNode.lhs(); - final Node rhs = binaryNode.rhs(); + final Expression lhs = binaryNode.lhs(); + final Expression rhs = binaryNode.rhs(); //detect ident cmp int_literal if (lhs instanceof IdentNode && rhs instanceof LiteralNode && ((LiteralNode)rhs).getType().isInteger()) { diff --git a/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java b/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java new file mode 100644 index 00000000000..08dfedf1a9f --- /dev/null +++ b/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2010-2013, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 jdk.nashorn.internal.codegen; + +import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; +import static jdk.nashorn.internal.codegen.types.Type.OBJECT; + +import java.util.List; +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.Symbol; +import jdk.nashorn.internal.runtime.Property; +import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.scripts.JO; + +/** + * An object creator that uses spill properties. + */ +public class SpillObjectCreator extends ObjectCreator { + + private final List values; + + /** + * Constructor + * + * @param codegen code generator + * @param keys keys for fields in object + * @param symbols symbols for fields in object + * @param values list of values corresponding to keys + */ + protected SpillObjectCreator(final CodeGenerator codegen, final List keys, final List symbols, final List values) { + super(codegen, keys, symbols, false, false); + this.values = values; + makeMap(); + } + + @Override + protected void makeObject(final MethodEmitter method) { + assert !isScope() : "spill scope objects are not currently supported"; + + final int length = keys.size(); + final Object[] presetValues = new Object[propertyMap.size()]; + final Class clazz = JO.class; + + // Compute constant values + for (int i = 0; i < length; i++) { + final String key = keys.get(i); + final Property property = propertyMap.findProperty(key); + + if (property != null) { + presetValues[property.getSlot()] = LiteralNode.objectAsConstant(values.get(i)); + } + } + + method._new(clazz).dup(); + codegen.loadConstant(propertyMap); + + method.invoke(constructorNoLookup(JO.class, PropertyMap.class)); + + method.dup(); + codegen.loadConstant(presetValues); + + // Create properties with non-constant values + for (int i = 0; i < length; i++) { + final String key = keys.get(i); + final Property property = propertyMap.findProperty(key); + + if (property != null && presetValues[property.getSlot()] == LiteralNode.POSTSET_MARKER) { + method.dup(); + method.load(property.getSlot()); + codegen.load(values.get(i)).convert(OBJECT); + method.arraystore(); + presetValues[property.getSlot()] = null; + } + } + + method.putField(Type.typeFor(ScriptObject.class).getInternalName(), "spill", Type.OBJECT_ARRAY.getDescriptor()); + final int callSiteFlags = codegen.getCallSiteFlags(); + + // Assign properties with valid array index keys + for (int i = 0; i < length; i++) { + final String key = keys.get(i); + final Property property = propertyMap.findProperty(key); + final Expression value = values.get(i); + + if (property == null && value != null) { + method.dup(); + method.load(keys.get(i)); + codegen.load(value); + method.dynamicSetIndex(callSiteFlags); + } + } + } + + @Override + protected PropertyMap makeMap() { + assert propertyMap == null : "property map already initialized"; + + propertyMap = new MapCreator(JO.class, keys, symbols) { + @Override + protected int getPropertyFlags(Symbol symbol, boolean hasArguments) { + return super.getPropertyFlags(symbol, hasArguments) | Property.IS_SPILL | Property.IS_ALWAYS_OBJECT; + } + }.makeSpillMap(false); + + return propertyMap; + } +} diff --git a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java index e724ce261c3..a35c6dfdcf1 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/Splitter.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; @@ -221,14 +220,13 @@ final class Splitter extends NodeVisitor { * @return New split node. */ private SplitNode createBlockSplitNode(final Block parent, final FunctionNode function, final List statements, final long weight) { - final int lineNumber = parent.getLineNumber(); final long token = parent.getToken(); final int finish = parent.getFinish(); final String name = function.uniqueName(SPLIT_PREFIX.symbolName()); - final Block newBlock = new Block(lineNumber, token, finish, statements); + final Block newBlock = new Block(token, finish, statements); - return new SplitNode(lineNumber, name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT)); + return new SplitNode(name, newBlock, compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT)); } @Override diff --git a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java index 1adef12bb3c..57b3bafa797 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/WeighNodes.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen; import java.util.List; import java.util.Map; - import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; @@ -36,7 +35,7 @@ import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; -import jdk.nashorn.internal.ir.ExecuteNode; +import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; @@ -158,8 +157,8 @@ final class WeighNodes extends NodeOperatorVisitor { } @Override - public Node leaveExecuteNode(final ExecuteNode executeNode) { - return executeNode; + public Node leaveExpressionStatement(final ExpressionStatement expressionStatement) { + return expressionStatement; } @Override diff --git a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java index ab32a779703..c1e8bfbb432 100644 --- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java +++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java @@ -47,9 +47,8 @@ import static jdk.internal.org.objectweb.asm.Opcodes.T_INT; import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG; import java.lang.invoke.MethodHandle; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import jdk.internal.org.objectweb.asm.MethodVisitor; import jdk.nashorn.internal.codegen.CompilerConstants.Call; @@ -548,19 +547,19 @@ public abstract class Type implements Comparable, BytecodeOps { * @return the Type representing this class */ public static Type typeFor(final Class clazz) { - Type type = cache.get(clazz); - - if (type == null) { - assert !clazz.isPrimitive() || clazz == void.class; - if (clazz.isArray()) { - type = new ArrayType(clazz); - } else { - type = new ObjectType(clazz); - } - cache.put(clazz, type); + final Type type = cache.get(clazz); + if(type != null) { + return type; } - - return type; + assert !clazz.isPrimitive() || clazz == void.class; + final Type newType; + if (clazz.isArray()) { + newType = new ArrayType(clazz); + } else { + newType = new ObjectType(clazz); + } + final Type existingType = cache.putIfAbsent(clazz, newType); + return existingType == null ? newType : existingType; } @Override @@ -663,35 +662,38 @@ public abstract class Type implements Comparable, BytecodeOps { } } + /** Mappings between java classes and their Type singletons */ + private static final ConcurrentMap, Type> cache = new ConcurrentHashMap<>(); + /** * This is the boolean singleton, used for all boolean types */ - public static final Type BOOLEAN = new BooleanType(); + public static final Type BOOLEAN = putInCache(new BooleanType()); /** * This is an integer type, i.e INT, INT32. */ - public static final Type INT = new IntType(); + public static final Type INT = putInCache(new IntType()); /** * This is the number singleton, used for all number types */ - public static final Type NUMBER = new NumberType(); + public static final Type NUMBER = putInCache(new NumberType()); /** * This is the long singleton, used for all long types */ - public static final Type LONG = new LongType(); + public static final Type LONG = putInCache(new LongType()); /** * A string singleton */ - public static final Type STRING = new ObjectType(String.class); + public static final Type STRING = putInCache(new ObjectType(String.class)); /** * This is the object singleton, used for all object types */ - public static final Type OBJECT = new ObjectType(); + public static final Type OBJECT = putInCache(new ObjectType()); /** * This is the singleton for integer arrays @@ -775,13 +777,13 @@ public abstract class Type implements Comparable, BytecodeOps { }; /** Singleton for method handle arrays used for properties etc. */ - public static final ArrayType METHODHANDLE_ARRAY = new ArrayType(MethodHandle[].class); + public static final ArrayType METHODHANDLE_ARRAY = putInCache(new ArrayType(MethodHandle[].class)); /** This is the singleton for string arrays */ - public static final ArrayType STRING_ARRAY = new ArrayType(String[].class); + public static final ArrayType STRING_ARRAY = putInCache(new ArrayType(String[].class)); /** This is the singleton for object arrays */ - public static final ArrayType OBJECT_ARRAY = new ArrayType(Object[].class); + public static final ArrayType OBJECT_ARRAY = putInCache(new ArrayType(Object[].class)); /** This type, always an object type, just a toString override */ public static final Type THIS = new ObjectType() { @@ -855,18 +857,8 @@ public abstract class Type implements Comparable, BytecodeOps { } }; - /** Mappings between java classes and their Type singletons */ - private static final Map, Type> cache = Collections.synchronizedMap(new HashMap, Type>()); - - //TODO may need to be cleared, as all types are retained throughout code generation - static { - cache.put(BOOLEAN.getTypeClass(), BOOLEAN); - cache.put(INT.getTypeClass(), INT); - cache.put(LONG.getTypeClass(), LONG); - cache.put(NUMBER.getTypeClass(), NUMBER); - cache.put(STRING.getTypeClass(), STRING); - cache.put(OBJECT.getTypeClass(), OBJECT); - cache.put(OBJECT_ARRAY.getTypeClass(), OBJECT_ARRAY); + private static T putInCache(T type) { + cache.put(type.getTypeClass(), type); + return type; } - } diff --git a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java index eecc5713541..55b0aa3feb3 100644 --- a/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java +++ b/nashorn/src/jdk/nashorn/internal/ir/AccessNode.java @@ -45,12 +45,12 @@ public final class AccessNode extends BaseNode { * @param base base node * @param property property */ - public AccessNode(final long token, final int finish, final Node base, final IdentNode property) { + public AccessNode(final long token, final int finish, final Expression base, final IdentNode property) { super(token, finish, base, false, false); this.property = property.setIsPropertyName(); } - private AccessNode(final AccessNode accessNode, final Node base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) { + private AccessNode(final AccessNode accessNode, final Expression base, final IdentNode property, final boolean isFunction, final boolean hasCallSiteType) { super(accessNode, base, isFunction, hasCallSiteType); this.property = property; } @@ -63,7 +63,7 @@ public final class AccessNode extends BaseNode { public Node accept(final NodeVisitor visitor) { if (visitor.enterAccessNode(this)) { return visitor.leaveAccessNode( - setBase(base.accept(visitor)). + setBase((Expression)base.accept(visitor)). setProperty((IdentNode)property.accept(visitor))); } return this; @@ -103,7 +103,7 @@ public final class AccessNode extends BaseNode { return property; } - private AccessNode setBase(final Node base) { + private AccessNode setBase(final Expression base) { if (this.base == base) { return this; } diff --git a/nashorn/src/jdk/nashorn/internal/ir/Assignment.java b/nashorn/src/jdk/nashorn/internal/ir/Assignment.java index 0c531bc2906..093b0d80a7d 100644 --- a/nashorn/src/jdk/nashorn/internal/ir/Assignment.java +++ b/nashorn/src/jdk/nashorn/internal/ir/Assignment.java @@ -31,7 +31,7 @@ package jdk.nashorn.internal.ir; * * @param the destination type */ -public interface Assignment { +public interface Assignment { /** * Get assignment destination @@ -45,7 +45,7 @@ public interface Assignment { * * @return get the assignment source node */ - public Node getAssignmentSource(); + public Expression getAssignmentSource(); /** * Set assignment destination node. diff --git a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java index a1b7c0eed7d..f945ef35711 100644 --- a/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java +++ b/nashorn/src/jdk/nashorn/internal/ir/BaseNode.java @@ -26,6 +26,7 @@ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; + import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -37,10 +38,10 @@ import jdk.nashorn.internal.ir.annotations.Immutable; * @see IndexNode */ @Immutable -public abstract class BaseNode extends Node implements FunctionCall, TypeOverride { +public abstract class BaseNode extends Expression implements FunctionCall, TypeOverride { /** Base Node. */ - protected final Node base; + protected final Expression base; private final boolean isFunction; @@ -55,7 +56,7 @@ public abstract class BaseNode extends Node implements FunctionCall, TypeOverrid * @param isFunction is this a function * @param hasCallSiteType does this access have a callsite type */ - public BaseNode(final long token, final int finish, final Node base, final boolean isFunction, final boolean hasCallSiteType) { + public BaseNode(final long token, final int finish, final Expression base, final boolean isFunction, final boolean hasCallSiteType) { super(token, base.getStart(), finish); this.base = base; this.isFunction = isFunction; @@ -69,7 +70,7 @@ public abstract class BaseNode extends Node implements FunctionCall, TypeOverrid * @param isFunction is this a function * @param hasCallSiteType does this access have a callsite type */ - protected BaseNode(final BaseNode baseNode, final Node base, final boolean isFunction, final boolean hasCallSiteType) { + protected BaseNode(final BaseNode baseNode, final Expression base, final boolean isFunction, final boolean hasCallSiteType) { super(baseNode); this.base = base; this.isFunction = isFunction; @@ -80,7 +81,7 @@ public abstract class BaseNode extends Node implements FunctionCall, TypeOverrid * Get the base node for this access * @return the base node */ - public Node getBase() { + public Expression getBase() { return base; } diff --git a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java index 4afa1945eae..1576fb9316e 100644 --- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java +++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java @@ -34,11 +34,11 @@ import jdk.nashorn.internal.parser.TokenType; * BinaryNode nodes represent two operand operations. */ @Immutable -public final class BinaryNode extends Node implements Assignment { +public final class BinaryNode extends Expression implements Assignment { /** Left hand side argument. */ - private final Node lhs; + private final Expression lhs; - private final Node rhs; + private final Expression rhs; /** * Constructor @@ -47,13 +47,13 @@ public final class BinaryNode extends Node implements Assignment { * @param lhs left hand side * @param rhs right hand side */ - public BinaryNode(final long token, final Node lhs, final Node rhs) { + public BinaryNode(final long token, final Expression lhs, final Expression rhs) { super(token, lhs.getStart(), rhs.getFinish()); this.lhs = lhs; this.rhs = rhs; } - private BinaryNode(final BinaryNode binaryNode, final Node lhs, final Node rhs) { + private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs) { super(binaryNode); this.lhs = lhs; this.rhs = rhs; @@ -141,17 +141,17 @@ public final class BinaryNode extends Node implements Assignment { } @Override - public Node getAssignmentDest() { + public Expression getAssignmentDest() { return isAssignment() ? lhs() : null; } @Override - public Node setAssignmentDest(Node n) { + public BinaryNode setAssignmentDest(Expression n) { return setLHS(n); } @Override - public Node getAssignmentSource() { + public Expression getAssignmentSource() { return rhs(); } @@ -162,7 +162,7 @@ public final class BinaryNode extends Node implements Assignment { @Override public Node accept(final NodeVisitor visitor) { if (visitor.enterBinaryNode(this)) { - return visitor.leaveBinaryNode(setLHS(lhs.accept(visitor)).setRHS(rhs.accept(visitor))); + return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor))); } return this; @@ -218,7 +218,7 @@ public final class BinaryNode extends Node implements Assignment { * Get the left hand side expression for this node * @return the left hand side expression */ - public Node lhs() { + public Expression lhs() { return lhs; } @@ -226,7 +226,7 @@ public final class BinaryNode extends Node implements Assignment { * Get the right hand side expression for this node * @return the left hand side expression */ - public Node rhs() { + public Expression rhs() { return rhs; } @@ -235,7 +235,7 @@ public final class BinaryNode extends Node implements Assignment { * @param lhs new left hand side expression * @return a node equivalent to this one except for the requested change. */ - public BinaryNode setLHS(final Node lhs) { + public BinaryNode setLHS(final Expression lhs) { if (this.lhs == lhs) { return this; } @@ -247,7 +247,7 @@ public final class BinaryNode extends Node implements Assignment { * @param rhs new left hand side expression * @return a node equivalent to this one except for the requested change. */ - public BinaryNode setRHS(final Node rhs) { + public BinaryNode setRHS(final Expression rhs) { if (this.rhs == rhs) { return this; } diff --git a/nashorn/src/jdk/nashorn/internal/ir/Block.java b/nashorn/src/jdk/nashorn/internal/ir/Block.java index 71692144167..c411401e710 100644 --- a/nashorn/src/jdk/nashorn/internal/ir/Block.java +++ b/nashorn/src/jdk/nashorn/internal/ir/Block.java @@ -38,11 +38,10 @@ import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; /** - * IR representation for a list of statements and functions. All provides the - * basis for script body. + * IR representation for a list of statements. */ @Immutable -public class Block extends BreakableNode implements Flags { +public class Block extends Node implements BreakableNode, Flags { /** List of statements */ protected final List statements; @@ -52,6 +51,9 @@ public class Block extends BreakableNode implements Flags { /** Entry label. */ protected final Label entryLabel; + /** Break label. */ + private final Label breakLabel; + /** Does the block/function need a new scope? */ protected final int flags; @@ -76,17 +78,17 @@ public class Block extends BreakableNode implements Flags { /** * Constructor * - * @param lineNumber line number * @param token token * @param finish finish * @param statements statements */ - public Block(final int lineNumber, final long token, final int finish, final Statement... statements) { - super(lineNumber, token, finish, new Label("block_break")); + public Block(final long token, final int finish, final Statement... statements) { + super(token, finish); this.statements = Arrays.asList(statements); this.symbols = new LinkedHashMap<>(); this.entryLabel = new Label("block_entry"); + this.breakLabel = new Label("block_break"); this.flags = 0; } @@ -98,8 +100,8 @@ public class Block extends BreakableNode implements Flags { * @param finish finish * @param statements statements */ - public Block(final int lineNumber, final long token, final int finish, final List statements) { - this(lineNumber, token, finish, statements.toArray(new Statement[statements.size()])); + public Block(final long token, final int finish, final List statements) { + this(token, finish, statements.toArray(new Statement[statements.size()])); } private Block(final Block block, final int finish, final List statements, final int flags, final Map symbols) { @@ -108,6 +110,7 @@ public class Block extends BreakableNode implements Flags { this.flags = flags; this.symbols = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now this.entryLabel = new Label(block.entryLabel); + this.breakLabel = new Label(block.breakLabel); this.finish = finish; } @@ -223,6 +226,11 @@ public class Block extends BreakableNode implements Flags { return entryLabel; } + @Override + public Label getBreakLabel() { + return breakLabel; + } + /** * Get the list of statements in this block * @@ -322,7 +330,17 @@ public class Block extends BreakableNode implements Flags { } @Override - protected boolean isBreakableWithoutLabel() { + public boolean isBreakableWithoutLabel() { return false; } + + @Override + public List