提交 b17e9bbd 编写于 作者: T tschatzl

Merge

...@@ -375,3 +375,5 @@ acac3bde66b2c22791c257a8d99611d6d08c6713 jdk8-b105 ...@@ -375,3 +375,5 @@ acac3bde66b2c22791c257a8d99611d6d08c6713 jdk8-b105
18b4798adbc42c6fa16f5ecb7d5cd3ca130754bf hs25-b48 18b4798adbc42c6fa16f5ecb7d5cd3ca130754bf hs25-b48
aed585cafc0d9655726af6d1e1081d1c94cb3b5c jdk8-b106 aed585cafc0d9655726af6d1e1081d1c94cb3b5c jdk8-b106
50794d8ac11c9579b41dec4de23b808fef9f34a1 hs25-b49 50794d8ac11c9579b41dec4de23b808fef9f34a1 hs25-b49
5b7f90aab3ad25a25b75b7b2bb18d5ae23d8231c jdk8-b107
a09fe9d1e016c285307507a5793bc4fa6215e9c9 hs25-b50
...@@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2013 ...@@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2013
HS_MAJOR_VER=25 HS_MAJOR_VER=25
HS_MINOR_VER=0 HS_MINOR_VER=0
HS_BUILD_NUMBER=50 HS_BUILD_NUMBER=51
JDK_MAJOR_VER=1 JDK_MAJOR_VER=1
JDK_MINOR_VER=8 JDK_MINOR_VER=8
......
...@@ -57,6 +57,7 @@ define_pd_global(intx, RegisterCostAreaRatio, 12000); ...@@ -57,6 +57,7 @@ define_pd_global(intx, RegisterCostAreaRatio, 12000);
define_pd_global(bool, UseTLAB, true); define_pd_global(bool, UseTLAB, true);
define_pd_global(bool, ResizeTLAB, true); define_pd_global(bool, ResizeTLAB, true);
define_pd_global(intx, LoopUnrollLimit, 60); // Design center runs on 1.3.1 define_pd_global(intx, LoopUnrollLimit, 60); // Design center runs on 1.3.1
define_pd_global(intx, MinJumpTableSize, 5);
// Peephole and CISC spilling both break the graph, and so makes the // Peephole and CISC spilling both break the graph, and so makes the
// scheduler sick. // scheduler sick.
......
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
// Sets the default values for platform dependent flags used by the server compiler. // Sets the default values for platform dependent flags used by the server compiler.
// (see c2_globals.hpp). Alpha-sorted. // (see c2_globals.hpp). Alpha-sorted.
define_pd_global(bool, BackgroundCompilation, true); define_pd_global(bool, BackgroundCompilation, true);
define_pd_global(bool, UseTLAB, true); define_pd_global(bool, UseTLAB, true);
define_pd_global(bool, ResizeTLAB, true); define_pd_global(bool, ResizeTLAB, true);
...@@ -52,6 +51,7 @@ define_pd_global(intx, OnStackReplacePercentage, 140); ...@@ -52,6 +51,7 @@ define_pd_global(intx, OnStackReplacePercentage, 140);
define_pd_global(intx, ConditionalMoveLimit, 3); define_pd_global(intx, ConditionalMoveLimit, 3);
define_pd_global(intx, FLOATPRESSURE, 6); define_pd_global(intx, FLOATPRESSURE, 6);
define_pd_global(intx, FreqInlineSize, 325); define_pd_global(intx, FreqInlineSize, 325);
define_pd_global(intx, MinJumpTableSize, 10);
#ifdef AMD64 #ifdef AMD64
define_pd_global(intx, INTPRESSURE, 13); define_pd_global(intx, INTPRESSURE, 13);
define_pd_global(intx, InteriorEntryAlignment, 16); define_pd_global(intx, InteriorEntryAlignment, 16);
......
...@@ -24,13 +24,92 @@ ...@@ -24,13 +24,92 @@
#include "precompiled.hpp" #include "precompiled.hpp"
#include "ci/ciArray.hpp" #include "ci/ciArray.hpp"
#include "ci/ciArrayKlass.hpp"
#include "ci/ciConstant.hpp"
#include "ci/ciKlass.hpp" #include "ci/ciKlass.hpp"
#include "ci/ciUtilities.hpp" #include "ci/ciUtilities.hpp"
#include "oops/objArrayOop.hpp"
#include "oops/typeArrayOop.hpp"
// ciArray // ciArray
// //
// This class represents an arrayOop in the HotSpot virtual // This class represents an arrayOop in the HotSpot virtual
// machine. // machine.
static BasicType fixup_element_type(BasicType bt) {
if (bt == T_ARRAY) return T_OBJECT;
if (bt == T_BOOLEAN) return T_BYTE;
return bt;
}
ciConstant ciArray::element_value_impl(BasicType elembt,
arrayOop ary,
int index) {
if (ary == NULL)
return ciConstant();
assert(ary->is_array(), "");
if (index < 0 || index >= ary->length())
return ciConstant();
ArrayKlass* ak = (ArrayKlass*) ary->klass();
BasicType abt = ak->element_type();
if (fixup_element_type(elembt) !=
fixup_element_type(abt))
return ciConstant();
switch (elembt) {
case T_ARRAY:
case T_OBJECT:
{
assert(ary->is_objArray(), "");
objArrayOop objary = (objArrayOop) ary;
oop elem = objary->obj_at(index);
ciEnv* env = CURRENT_ENV;
ciObject* box = env->get_object(elem);
return ciConstant(T_OBJECT, box);
}
}
assert(ary->is_typeArray(), "");
typeArrayOop tary = (typeArrayOop) ary;
jint value = 0;
switch (elembt) {
case T_LONG: return ciConstant(tary->long_at(index));
case T_FLOAT: return ciConstant(tary->float_at(index));
case T_DOUBLE: return ciConstant(tary->double_at(index));
default: return ciConstant();
case T_BYTE: value = tary->byte_at(index); break;
case T_BOOLEAN: value = tary->byte_at(index) & 1; break;
case T_SHORT: value = tary->short_at(index); break;
case T_CHAR: value = tary->char_at(index); break;
case T_INT: value = tary->int_at(index); break;
}
return ciConstant(elembt, value);
}
// ------------------------------------------------------------------
// ciArray::element_value
//
// Current value of an element.
// Returns T_ILLEGAL if there is no element at the given index.
ciConstant ciArray::element_value(int index) {
BasicType elembt = element_basic_type();
GUARDED_VM_ENTRY(
return element_value_impl(elembt, get_arrayOop(), index);
)
}
// ------------------------------------------------------------------
// ciArray::element_value_by_offset
//
// Current value of an element at the specified offset.
// Returns T_ILLEGAL if there is no element at the given offset.
ciConstant ciArray::element_value_by_offset(intptr_t element_offset) {
BasicType elembt = element_basic_type();
intptr_t shift = exact_log2(type2aelembytes(elembt));
intptr_t header = arrayOopDesc::base_offset_in_bytes(elembt);
intptr_t index = (element_offset - header) >> shift;
intptr_t offset = header + ((intptr_t)index << shift);
if (offset != element_offset || index != (jint)index)
return ciConstant();
return element_value((jint) index);
}
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// ciArray::print_impl // ciArray::print_impl
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
#ifndef SHARE_VM_CI_CIARRAY_HPP #ifndef SHARE_VM_CI_CIARRAY_HPP
#define SHARE_VM_CI_CIARRAY_HPP #define SHARE_VM_CI_CIARRAY_HPP
#include "ci/ciArrayKlass.hpp"
#include "ci/ciConstant.hpp"
#include "ci/ciObject.hpp" #include "ci/ciObject.hpp"
#include "oops/arrayOop.hpp" #include "oops/arrayOop.hpp"
#include "oops/objArrayOop.hpp" #include "oops/objArrayOop.hpp"
...@@ -45,15 +47,30 @@ protected: ...@@ -45,15 +47,30 @@ protected:
ciArray(ciKlass* klass, int len) : ciObject(klass), _length(len) {} ciArray(ciKlass* klass, int len) : ciObject(klass), _length(len) {}
arrayOop get_arrayOop() { return (arrayOop)get_oop(); } arrayOop get_arrayOop() const { return (arrayOop)get_oop(); }
const char* type_string() { return "ciArray"; } const char* type_string() { return "ciArray"; }
void print_impl(outputStream* st); void print_impl(outputStream* st);
ciConstant element_value_impl(BasicType elembt, arrayOop ary, int index);
public: public:
int length() { return _length; } int length() { return _length; }
// Convenience routines.
ciArrayKlass* array_type() { return klass()->as_array_klass(); }
ciType* element_type() { return array_type()->element_type(); }
BasicType element_basic_type() { return element_type()->basic_type(); }
// Current value of an element.
// Returns T_ILLEGAL if there is no element at the given index.
ciConstant element_value(int index);
// Current value of an element at the specified offset.
// Returns T_ILLEGAL if there is no element at the given offset.
ciConstant element_value_by_offset(intptr_t element_offset);
// What kind of ciObject is this? // What kind of ciObject is this?
bool is_array() { return true; } bool is_array() { return true; }
bool is_java_object() { return true; } bool is_java_object() { return true; }
......
...@@ -41,7 +41,6 @@ private: ...@@ -41,7 +41,6 @@ private:
union { union {
jint _int; jint _int;
jlong _long; jlong _long;
jint _long_half[2];
jfloat _float; jfloat _float;
jdouble _double; jdouble _double;
ciObject* _object; ciObject* _object;
...@@ -111,6 +110,20 @@ public: ...@@ -111,6 +110,20 @@ public:
return _value._object; return _value._object;
} }
bool is_null_or_zero() const {
if (!is_java_primitive(basic_type())) {
return as_object()->is_null_object();
} else if (type2size[basic_type()] == 1) {
// treat float bits as int, to avoid comparison with -0 and NaN
return (_value._int == 0);
} else if (type2size[basic_type()] == 2) {
// treat double bits as long, to avoid comparison with -0 and NaN
return (_value._long == 0);
} else {
return false;
}
}
// Debugging output // Debugging output
void print(); void print();
}; };
......
...@@ -189,12 +189,14 @@ void ciField::initialize_from(fieldDescriptor* fd) { ...@@ -189,12 +189,14 @@ void ciField::initialize_from(fieldDescriptor* fd) {
_holder = CURRENT_ENV->get_instance_klass(fd->field_holder()); _holder = CURRENT_ENV->get_instance_klass(fd->field_holder());
// Check to see if the field is constant. // Check to see if the field is constant.
if (_holder->is_initialized() && this->is_final()) { bool is_final = this->is_final();
bool is_stable = FoldStableValues && this->is_stable();
if (_holder->is_initialized() && (is_final || is_stable)) {
if (!this->is_static()) { if (!this->is_static()) {
// A field can be constant if it's a final static field or if // A field can be constant if it's a final static field or if
// it's a final non-static field of a trusted class (classes in // it's a final non-static field of a trusted class (classes in
// java.lang.invoke and sun.invoke packages and subpackages). // java.lang.invoke and sun.invoke packages and subpackages).
if (trust_final_non_static_fields(_holder)) { if (is_stable || trust_final_non_static_fields(_holder)) {
_is_constant = true; _is_constant = true;
return; return;
} }
...@@ -227,7 +229,6 @@ void ciField::initialize_from(fieldDescriptor* fd) { ...@@ -227,7 +229,6 @@ void ciField::initialize_from(fieldDescriptor* fd) {
Handle mirror = k->java_mirror(); Handle mirror = k->java_mirror();
_is_constant = true;
switch(type()->basic_type()) { switch(type()->basic_type()) {
case T_BYTE: case T_BYTE:
_constant_value = ciConstant(type()->basic_type(), mirror->byte_field(_offset)); _constant_value = ciConstant(type()->basic_type(), mirror->byte_field(_offset));
...@@ -273,6 +274,12 @@ void ciField::initialize_from(fieldDescriptor* fd) { ...@@ -273,6 +274,12 @@ void ciField::initialize_from(fieldDescriptor* fd) {
} }
} }
} }
if (is_stable && _constant_value.is_null_or_zero()) {
// It is not a constant after all; treat it as uninitialized.
_is_constant = false;
} else {
_is_constant = true;
}
} else { } else {
_is_constant = false; _is_constant = false;
} }
...@@ -373,8 +380,11 @@ void ciField::print() { ...@@ -373,8 +380,11 @@ void ciField::print() {
tty->print(" signature="); tty->print(" signature=");
_signature->print_symbol(); _signature->print_symbol();
tty->print(" offset=%d type=", _offset); tty->print(" offset=%d type=", _offset);
if (_type != NULL) _type->print_name(); if (_type != NULL)
else tty->print("(reference)"); _type->print_name();
else
tty->print("(reference)");
tty->print(" flags=%04x", flags().as_int());
tty->print(" is_constant=%s", bool_to_str(_is_constant)); tty->print(" is_constant=%s", bool_to_str(_is_constant));
if (_is_constant && is_static()) { if (_is_constant && is_static()) {
tty->print(" constant_value="); tty->print(" constant_value=");
......
...@@ -139,7 +139,10 @@ public: ...@@ -139,7 +139,10 @@ public:
// non-constant fields. These are java.lang.System.in // non-constant fields. These are java.lang.System.in
// and java.lang.System.out. Abomination. // and java.lang.System.out. Abomination.
// //
// Note: the check for case 4 is not yet implemented. // A field is also considered constant if it is marked @Stable
// and is non-null (or non-zero, if a primitive).
// For non-static fields, the null/zero check must be
// arranged by the user, as constant_value().is_null_or_zero().
bool is_constant() { return _is_constant; } bool is_constant() { return _is_constant; }
// Get the constant value of this field. // Get the constant value of this field.
...@@ -173,6 +176,7 @@ public: ...@@ -173,6 +176,7 @@ public:
bool is_protected () { return flags().is_protected(); } bool is_protected () { return flags().is_protected(); }
bool is_static () { return flags().is_static(); } bool is_static () { return flags().is_static(); }
bool is_final () { return flags().is_final(); } bool is_final () { return flags().is_final(); }
bool is_stable () { return flags().is_stable(); }
bool is_volatile () { return flags().is_volatile(); } bool is_volatile () { return flags().is_volatile(); }
bool is_transient () { return flags().is_transient(); } bool is_transient () { return flags().is_transient(); }
......
...@@ -59,6 +59,7 @@ public: ...@@ -59,6 +59,7 @@ public:
bool is_interface () const { return (_flags & JVM_ACC_INTERFACE ) != 0; } bool is_interface () const { return (_flags & JVM_ACC_INTERFACE ) != 0; }
bool is_abstract () const { return (_flags & JVM_ACC_ABSTRACT ) != 0; } bool is_abstract () const { return (_flags & JVM_ACC_ABSTRACT ) != 0; }
bool is_strict () const { return (_flags & JVM_ACC_STRICT ) != 0; } bool is_strict () const { return (_flags & JVM_ACC_STRICT ) != 0; }
bool is_stable () const { return (_flags & JVM_ACC_FIELD_STABLE) != 0; }
// Conversion // Conversion
jint as_int() { return _flags; } jint as_int() { return _flags; }
......
...@@ -127,6 +127,8 @@ ciConstant ciInstance::field_value(ciField* field) { ...@@ -127,6 +127,8 @@ ciConstant ciInstance::field_value(ciField* field) {
ciConstant ciInstance::field_value_by_offset(int field_offset) { ciConstant ciInstance::field_value_by_offset(int field_offset) {
ciInstanceKlass* ik = klass()->as_instance_klass(); ciInstanceKlass* ik = klass()->as_instance_klass();
ciField* field = ik->get_field_by_offset(field_offset, false); ciField* field = ik->get_field_by_offset(field_offset, false);
if (field == NULL)
return ciConstant(); // T_ILLEGAL
return field_value(field); return field_value(field);
} }
......
...@@ -39,5 +39,10 @@ ...@@ -39,5 +39,10 @@
jchar ciTypeArray::char_at(int index) { jchar ciTypeArray::char_at(int index) {
VM_ENTRY_MARK; VM_ENTRY_MARK;
assert(index >= 0 && index < length(), "out of range"); assert(index >= 0 && index < length(), "out of range");
return get_typeArrayOop()->char_at(index); jchar c = get_typeArrayOop()->char_at(index);
#ifdef ASSERT
jchar d = element_value(index).as_char();
assert(c == d, "");
#endif //ASSERT
return c;
} }
...@@ -1774,6 +1774,10 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d ...@@ -1774,6 +1774,10 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d
if (_location != _in_method) break; // only allow for methods if (_location != _in_method) break; // only allow for methods
if (!privileged) break; // only allow in privileged code if (!privileged) break; // only allow in privileged code
return _method_LambdaForm_Hidden; return _method_LambdaForm_Hidden;
case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_invoke_Stable_signature):
if (_location != _in_field) break; // only allow for fields
if (!privileged) break; // only allow in privileged code
return _field_Stable;
case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature): case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature):
if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes
if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges
...@@ -1786,6 +1790,8 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d ...@@ -1786,6 +1790,8 @@ ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_d
void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) { void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) {
if (is_contended()) if (is_contended())
f->set_contended_group(contended_group()); f->set_contended_group(contended_group());
if (is_stable())
f->set_stable(true);
} }
ClassFileParser::FieldAnnotationCollector::~FieldAnnotationCollector() { ClassFileParser::FieldAnnotationCollector::~FieldAnnotationCollector() {
......
...@@ -125,6 +125,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { ...@@ -125,6 +125,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
_method_LambdaForm_Compiled, _method_LambdaForm_Compiled,
_method_LambdaForm_Hidden, _method_LambdaForm_Hidden,
_sun_misc_Contended, _sun_misc_Contended,
_field_Stable,
_annotation_LIMIT _annotation_LIMIT
}; };
const Location _location; const Location _location;
...@@ -143,14 +144,23 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { ...@@ -143,14 +144,23 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC {
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob"); assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob");
_annotations_present |= nth_bit((int)id); _annotations_present |= nth_bit((int)id);
} }
void remove_annotation(ID id) {
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob");
_annotations_present &= ~nth_bit((int)id);
}
// Report if the annotation is present. // Report if the annotation is present.
bool has_any_annotations() { return _annotations_present != 0; } bool has_any_annotations() const { return _annotations_present != 0; }
bool has_annotation(ID id) { return (nth_bit((int)id) & _annotations_present) != 0; } bool has_annotation(ID id) const { return (nth_bit((int)id) & _annotations_present) != 0; }
void set_contended_group(u2 group) { _contended_group = group; } void set_contended_group(u2 group) { _contended_group = group; }
u2 contended_group() { return _contended_group; } u2 contended_group() const { return _contended_group; }
bool is_contended() const { return has_annotation(_sun_misc_Contended); }
bool is_contended() { return has_annotation(_sun_misc_Contended); } void set_stable(bool stable) { set_annotation(_field_Stable); }
bool is_stable() const { return has_annotation(_field_Stable); }
}; };
// This class also doubles as a holder for metadata cleanup. // This class also doubles as a holder for metadata cleanup.
......
...@@ -270,6 +270,7 @@ ...@@ -270,6 +270,7 @@
template(java_lang_invoke_LambdaForm, "java/lang/invoke/LambdaForm") \ template(java_lang_invoke_LambdaForm, "java/lang/invoke/LambdaForm") \
template(java_lang_invoke_ForceInline_signature, "Ljava/lang/invoke/ForceInline;") \ template(java_lang_invoke_ForceInline_signature, "Ljava/lang/invoke/ForceInline;") \
template(java_lang_invoke_DontInline_signature, "Ljava/lang/invoke/DontInline;") \ template(java_lang_invoke_DontInline_signature, "Ljava/lang/invoke/DontInline;") \
template(sun_invoke_Stable_signature, "Lsun/invoke/Stable;") \
template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \ template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \
template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \ template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \
template(java_lang_invoke_MagicLambdaImpl, "java/lang/invoke/MagicLambdaImpl") \ template(java_lang_invoke_MagicLambdaImpl, "java/lang/invoke/MagicLambdaImpl") \
......
...@@ -240,6 +240,14 @@ class FieldInfo VALUE_OBJ_CLASS_SPEC { ...@@ -240,6 +240,14 @@ class FieldInfo VALUE_OBJ_CLASS_SPEC {
return (access_flags() & JVM_ACC_FIELD_INTERNAL) != 0; return (access_flags() & JVM_ACC_FIELD_INTERNAL) != 0;
} }
bool is_stable() const {
return (access_flags() & JVM_ACC_FIELD_STABLE) != 0;
}
void set_stable(bool z) {
if (z) _shorts[access_flags_offset] |= JVM_ACC_FIELD_STABLE;
else _shorts[access_flags_offset] &= ~JVM_ACC_FIELD_STABLE;
}
Symbol* lookup_symbol(int symbol_index) const { Symbol* lookup_symbol(int symbol_index) const {
assert(is_internal(), "only internal fields"); assert(is_internal(), "only internal fields");
return vmSymbols::symbol_at((vmSymbols::SID)symbol_index); return vmSymbols::symbol_at((vmSymbols::SID)symbol_index);
......
...@@ -421,7 +421,7 @@ ...@@ -421,7 +421,7 @@
product(bool, UseDivMod, true, \ product(bool, UseDivMod, true, \
"Use combined DivMod instruction if available") \ "Use combined DivMod instruction if available") \
\ \
product(intx, MinJumpTableSize, 18, \ product_pd(intx, MinJumpTableSize, \
"Minimum number of targets in a generated jump table") \ "Minimum number of targets in a generated jump table") \
\ \
product(intx, MaxJumpTableSize, 65000, \ product(intx, MaxJumpTableSize, 65000, \
...@@ -448,6 +448,9 @@ ...@@ -448,6 +448,9 @@
product(bool, EliminateAutoBox, true, \ product(bool, EliminateAutoBox, true, \
"Control optimizations for autobox elimination") \ "Control optimizations for autobox elimination") \
\ \
experimental(bool, UseImplicitStableValues, false, \
"Mark well-known stable fields as such (e.g. String.value)") \
\
product(intx, AutoBoxCacheMax, 128, \ product(intx, AutoBoxCacheMax, 128, \
"Sets max value cached by the java.lang.Integer autobox cache") \ "Sets max value cached by the java.lang.Integer autobox cache") \
\ \
......
...@@ -1297,6 +1297,10 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const { ...@@ -1297,6 +1297,10 @@ const TypePtr *Compile::flatten_alias_type( const TypePtr *tj ) const {
// Array pointers need some flattening // Array pointers need some flattening
const TypeAryPtr *ta = tj->isa_aryptr(); const TypeAryPtr *ta = tj->isa_aryptr();
if (ta && ta->is_stable()) {
// Erase stability property for alias analysis.
tj = ta = ta->cast_to_stable(false);
}
if( ta && is_known_inst ) { if( ta && is_known_inst ) {
if ( offset != Type::OffsetBot && if ( offset != Type::OffsetBot &&
offset > arrayOopDesc::length_offset_in_bytes() ) { offset > arrayOopDesc::length_offset_in_bytes() ) {
...@@ -1497,6 +1501,7 @@ void Compile::AliasType::Init(int i, const TypePtr* at) { ...@@ -1497,6 +1501,7 @@ void Compile::AliasType::Init(int i, const TypePtr* at) {
_index = i; _index = i;
_adr_type = at; _adr_type = at;
_field = NULL; _field = NULL;
_element = NULL;
_is_rewritable = true; // default _is_rewritable = true; // default
const TypeOopPtr *atoop = (at != NULL) ? at->isa_oopptr() : NULL; const TypeOopPtr *atoop = (at != NULL) ? at->isa_oopptr() : NULL;
if (atoop != NULL && atoop->is_known_instance()) { if (atoop != NULL && atoop->is_known_instance()) {
...@@ -1615,6 +1620,16 @@ Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_cr ...@@ -1615,6 +1620,16 @@ Compile::AliasType* Compile::find_alias_type(const TypePtr* adr_type, bool no_cr
&& flat->is_instptr()->klass() == env()->Class_klass()) && flat->is_instptr()->klass() == env()->Class_klass())
alias_type(idx)->set_rewritable(false); alias_type(idx)->set_rewritable(false);
} }
if (flat->isa_aryptr()) {
#ifdef ASSERT
const int header_size_min = arrayOopDesc::base_offset_in_bytes(T_BYTE);
// (T_BYTE has the weakest alignment and size restrictions...)
assert(flat->offset() < header_size_min, "array body reference must be OffsetBot");
#endif
if (flat->offset() == TypePtr::OffsetBot) {
alias_type(idx)->set_element(flat->is_aryptr()->elem());
}
}
if (flat->isa_klassptr()) { if (flat->isa_klassptr()) {
if (flat->offset() == in_bytes(Klass::super_check_offset_offset())) if (flat->offset() == in_bytes(Klass::super_check_offset_offset()))
alias_type(idx)->set_rewritable(false); alias_type(idx)->set_rewritable(false);
...@@ -1677,7 +1692,7 @@ Compile::AliasType* Compile::alias_type(ciField* field) { ...@@ -1677,7 +1692,7 @@ Compile::AliasType* Compile::alias_type(ciField* field) {
else else
t = TypeOopPtr::make_from_klass_raw(field->holder()); t = TypeOopPtr::make_from_klass_raw(field->holder());
AliasType* atp = alias_type(t->add_offset(field->offset_in_bytes()), field); AliasType* atp = alias_type(t->add_offset(field->offset_in_bytes()), field);
assert(field->is_final() == !atp->is_rewritable(), "must get the rewritable bits correct"); assert((field->is_final() || field->is_stable()) == !atp->is_rewritable(), "must get the rewritable bits correct");
return atp; return atp;
} }
......
...@@ -72,6 +72,7 @@ class Scope; ...@@ -72,6 +72,7 @@ class Scope;
class StartNode; class StartNode;
class SafePointNode; class SafePointNode;
class JVMState; class JVMState;
class Type;
class TypeData; class TypeData;
class TypePtr; class TypePtr;
class TypeOopPtr; class TypeOopPtr;
...@@ -119,6 +120,7 @@ class Compile : public Phase { ...@@ -119,6 +120,7 @@ class Compile : public Phase {
int _index; // unique index, used with MergeMemNode int _index; // unique index, used with MergeMemNode
const TypePtr* _adr_type; // normalized address type const TypePtr* _adr_type; // normalized address type
ciField* _field; // relevant instance field, or null if none ciField* _field; // relevant instance field, or null if none
const Type* _element; // relevant array element type, or null if none
bool _is_rewritable; // false if the memory is write-once only bool _is_rewritable; // false if the memory is write-once only
int _general_index; // if this is type is an instance, the general int _general_index; // if this is type is an instance, the general
// type that this is an instance of // type that this is an instance of
...@@ -129,6 +131,7 @@ class Compile : public Phase { ...@@ -129,6 +131,7 @@ class Compile : public Phase {
int index() const { return _index; } int index() const { return _index; }
const TypePtr* adr_type() const { return _adr_type; } const TypePtr* adr_type() const { return _adr_type; }
ciField* field() const { return _field; } ciField* field() const { return _field; }
const Type* element() const { return _element; }
bool is_rewritable() const { return _is_rewritable; } bool is_rewritable() const { return _is_rewritable; }
bool is_volatile() const { return (_field ? _field->is_volatile() : false); } bool is_volatile() const { return (_field ? _field->is_volatile() : false); }
int general_index() const { return (_general_index != 0) ? _general_index : _index; } int general_index() const { return (_general_index != 0) ? _general_index : _index; }
...@@ -137,7 +140,14 @@ class Compile : public Phase { ...@@ -137,7 +140,14 @@ class Compile : public Phase {
void set_field(ciField* f) { void set_field(ciField* f) {
assert(!_field,""); assert(!_field,"");
_field = f; _field = f;
if (f->is_final()) _is_rewritable = false; if (f->is_final() || f->is_stable()) {
// In the case of @Stable, multiple writes are possible but may be assumed to be no-ops.
_is_rewritable = false;
}
}
void set_element(const Type* e) {
assert(_element == NULL, "");
_element = e;
} }
void print_on(outputStream* st) PRODUCT_RETURN; void print_on(outputStream* st) PRODUCT_RETURN;
......
...@@ -3825,8 +3825,13 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) { ...@@ -3825,8 +3825,13 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
TypeAry::make(TypeInt::CHAR,TypeInt::POS), TypeAry::make(TypeInt::CHAR,TypeInt::POS),
ciTypeArrayKlass::make(T_CHAR), true, 0); ciTypeArrayKlass::make(T_CHAR), true, 0);
int value_field_idx = C->get_alias_index(value_field_type); int value_field_idx = C->get_alias_index(value_field_type);
return make_load(ctrl, basic_plus_adr(str, str, value_offset), Node* load = make_load(ctrl, basic_plus_adr(str, str, value_offset),
value_type, T_OBJECT, value_field_idx); value_type, T_OBJECT, value_field_idx);
// String.value field is known to be @Stable.
if (UseImplicitStableValues) {
load = cast_array_to_stable(load, value_type);
}
return load;
} }
void GraphKit::store_String_offset(Node* ctrl, Node* str, Node* value) { void GraphKit::store_String_offset(Node* ctrl, Node* str, Node* value) {
...@@ -3844,9 +3849,6 @@ void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) { ...@@ -3844,9 +3849,6 @@ void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) {
const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
false, NULL, 0); false, NULL, 0);
const TypePtr* value_field_type = string_type->add_offset(value_offset); const TypePtr* value_field_type = string_type->add_offset(value_offset);
const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull,
TypeAry::make(TypeInt::CHAR,TypeInt::POS),
ciTypeArrayKlass::make(T_CHAR), true, 0);
int value_field_idx = C->get_alias_index(value_field_type); int value_field_idx = C->get_alias_index(value_field_type);
store_to_memory(ctrl, basic_plus_adr(str, value_offset), store_to_memory(ctrl, basic_plus_adr(str, value_offset),
value, T_OBJECT, value_field_idx); value, T_OBJECT, value_field_idx);
...@@ -3861,3 +3863,9 @@ void GraphKit::store_String_length(Node* ctrl, Node* str, Node* value) { ...@@ -3861,3 +3863,9 @@ void GraphKit::store_String_length(Node* ctrl, Node* str, Node* value) {
store_to_memory(ctrl, basic_plus_adr(str, count_offset), store_to_memory(ctrl, basic_plus_adr(str, count_offset),
value, T_INT, count_field_idx); value, T_INT, count_field_idx);
} }
Node* GraphKit::cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type) {
// Reify the property as a CastPP node in Ideal graph to comply with monotonicity
// assumption of CCP analysis.
return _gvn.transform(new(C) CastPPNode(ary, ary_type->cast_to_stable(true)));
}
...@@ -836,6 +836,9 @@ class GraphKit : public Phase { ...@@ -836,6 +836,9 @@ class GraphKit : public Phase {
// Insert a loop predicate into the graph // Insert a loop predicate into the graph
void add_predicate(int nargs = 0); void add_predicate(int nargs = 0);
void add_predicate_impl(Deoptimization::DeoptReason reason, int nargs); void add_predicate_impl(Deoptimization::DeoptReason reason, int nargs);
// Produce new array node of stable type
Node* cast_array_to_stable(Node* ary, const TypeAryPtr* ary_type);
}; };
// Helper class to support building of control flow branches. Upon // Helper class to support building of control flow branches. Upon
......
...@@ -1280,6 +1280,11 @@ Node* LibraryCallKit::string_indexOf(Node* string_object, ciTypeArray* target_ar ...@@ -1280,6 +1280,11 @@ Node* LibraryCallKit::string_indexOf(Node* string_object, ciTypeArray* target_ar
const TypeAry* target_array_type = TypeAry::make(TypeInt::CHAR, TypeInt::make(0, target_length, Type::WidenMin)); const TypeAry* target_array_type = TypeAry::make(TypeInt::CHAR, TypeInt::make(0, target_length, Type::WidenMin));
const TypeAryPtr* target_type = TypeAryPtr::make(TypePtr::BotPTR, target_array_type, target_array->klass(), true, Type::OffsetBot); const TypeAryPtr* target_type = TypeAryPtr::make(TypePtr::BotPTR, target_array_type, target_array->klass(), true, Type::OffsetBot);
// String.value field is known to be @Stable.
if (UseImplicitStableValues) {
target = cast_array_to_stable(target, target_type);
}
IdealKit kit(this, false, true); IdealKit kit(this, false, true);
#define __ kit. #define __ kit.
Node* zero = __ ConI(0); Node* zero = __ ConI(0);
......
...@@ -962,6 +962,19 @@ uint LoadNode::hash() const { ...@@ -962,6 +962,19 @@ uint LoadNode::hash() const {
return (uintptr_t)in(Control) + (uintptr_t)in(Memory) + (uintptr_t)in(Address); return (uintptr_t)in(Control) + (uintptr_t)in(Memory) + (uintptr_t)in(Address);
} }
static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp, bool eliminate_boxing) {
if ((atp != NULL) && (atp->index() >= Compile::AliasIdxRaw)) {
bool non_volatile = (atp->field() != NULL) && !atp->field()->is_volatile();
bool is_stable_ary = FoldStableValues &&
(tp != NULL) && (tp->isa_aryptr() != NULL) &&
tp->isa_aryptr()->is_stable();
return (eliminate_boxing && non_volatile) || is_stable_ary;
}
return false;
}
//---------------------------can_see_stored_value------------------------------ //---------------------------can_see_stored_value------------------------------
// This routine exists to make sure this set of tests is done the same // This routine exists to make sure this set of tests is done the same
// everywhere. We need to make a coordinated change: first LoadNode::Ideal // everywhere. We need to make a coordinated change: first LoadNode::Ideal
...@@ -976,11 +989,9 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { ...@@ -976,11 +989,9 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
const TypeInstPtr* tp = phase->type(ld_adr)->isa_instptr(); const TypeInstPtr* tp = phase->type(ld_adr)->isa_instptr();
Compile::AliasType* atp = (tp != NULL) ? phase->C->alias_type(tp) : NULL; Compile::AliasType* atp = (tp != NULL) ? phase->C->alias_type(tp) : NULL;
// This is more general than load from boxing objects. // This is more general than load from boxing objects.
if (phase->C->eliminate_boxing() && (atp != NULL) && if (skip_through_membars(atp, tp, phase->C->eliminate_boxing())) {
(atp->index() >= Compile::AliasIdxRaw) &&
(atp->field() != NULL) && !atp->field()->is_volatile()) {
uint alias_idx = atp->index(); uint alias_idx = atp->index();
bool final = atp->field()->is_final(); bool final = !atp->is_rewritable();
Node* result = NULL; Node* result = NULL;
Node* current = st; Node* current = st;
// Skip through chains of MemBarNodes checking the MergeMems for // Skip through chains of MemBarNodes checking the MergeMems for
...@@ -1015,7 +1026,6 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { ...@@ -1015,7 +1026,6 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
} }
} }
// Loop around twice in the case Load -> Initialize -> Store. // Loop around twice in the case Load -> Initialize -> Store.
// (See PhaseIterGVN::add_users_to_worklist, which knows about this case.) // (See PhaseIterGVN::add_users_to_worklist, which knows about this case.)
for (int trip = 0; trip <= 1; trip++) { for (int trip = 0; trip <= 1; trip++) {
...@@ -1577,6 +1587,40 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls, ...@@ -1577,6 +1587,40 @@ LoadNode::load_array_final_field(const TypeKlassPtr *tkls,
return NULL; return NULL;
} }
// Try to constant-fold a stable array element.
static const Type* fold_stable_ary_elem(const TypeAryPtr* ary, int off, BasicType loadbt) {
assert(ary->is_stable(), "array should be stable");
if (ary->const_oop() != NULL) {
// Decode the results of GraphKit::array_element_address.
ciArray* aobj = ary->const_oop()->as_array();
ciConstant con = aobj->element_value_by_offset(off);
if (con.basic_type() != T_ILLEGAL && !con.is_null_or_zero()) {
const Type* con_type = Type::make_from_constant(con);
if (con_type != NULL) {
if (con_type->isa_aryptr()) {
// Join with the array element type, in case it is also stable.
int dim = ary->stable_dimension();
con_type = con_type->is_aryptr()->cast_to_stable(true, dim-1);
}
if (loadbt == T_NARROWOOP && con_type->isa_oopptr()) {
con_type = con_type->make_narrowoop();
}
#ifndef PRODUCT
if (TraceIterativeGVN) {
tty->print("FoldStableValues: array element [off=%d]: con_type=", off);
con_type->dump(); tty->cr();
}
#endif //PRODUCT
return con_type;
}
}
}
return NULL;
}
//------------------------------Value----------------------------------------- //------------------------------Value-----------------------------------------
const Type *LoadNode::Value( PhaseTransform *phase ) const { const Type *LoadNode::Value( PhaseTransform *phase ) const {
// Either input is TOP ==> the result is TOP // Either input is TOP ==> the result is TOP
...@@ -1591,8 +1635,31 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const { ...@@ -1591,8 +1635,31 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
Compile* C = phase->C; Compile* C = phase->C;
// Try to guess loaded type from pointer type // Try to guess loaded type from pointer type
if (tp->base() == Type::AryPtr) { if (tp->isa_aryptr()) {
const Type *t = tp->is_aryptr()->elem(); const TypeAryPtr* ary = tp->is_aryptr();
const Type *t = ary->elem();
// Determine whether the reference is beyond the header or not, by comparing
// the offset against the offset of the start of the array's data.
// Different array types begin at slightly different offsets (12 vs. 16).
// We choose T_BYTE as an example base type that is least restrictive
// as to alignment, which will therefore produce the smallest
// possible base offset.
const int min_base_off = arrayOopDesc::base_offset_in_bytes(T_BYTE);
const bool off_beyond_header = ((uint)off >= (uint)min_base_off);
// Try to constant-fold a stable array element.
if (FoldStableValues && ary->is_stable()) {
// Make sure the reference is not into the header
if (off_beyond_header && off != Type::OffsetBot) {
assert(adr->is_AddP() && adr->in(AddPNode::Offset)->is_Con(), "offset is a constant");
const Type* con_type = fold_stable_ary_elem(ary, off, memory_type());
if (con_type != NULL) {
return con_type;
}
}
}
// Don't do this for integer types. There is only potential profit if // Don't do this for integer types. There is only potential profit if
// the element type t is lower than _type; that is, for int types, if _type is // the element type t is lower than _type; that is, for int types, if _type is
// more restrictive than t. This only happens here if one is short and the other // more restrictive than t. This only happens here if one is short and the other
...@@ -1613,14 +1680,7 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const { ...@@ -1613,14 +1680,7 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
&& Opcode() != Op_LoadKlass && Opcode() != Op_LoadNKlass) { && Opcode() != Op_LoadKlass && Opcode() != Op_LoadNKlass) {
// t might actually be lower than _type, if _type is a unique // t might actually be lower than _type, if _type is a unique
// concrete subclass of abstract class t. // concrete subclass of abstract class t.
// Make sure the reference is not into the header, by comparing if (off_beyond_header) { // is the offset beyond the header?
// the offset against the offset of the start of the array's data.
// Different array types begin at slightly different offsets (12 vs. 16).
// We choose T_BYTE as an example base type that is least restrictive
// as to alignment, which will therefore produce the smallest
// possible base offset.
const int min_base_off = arrayOopDesc::base_offset_in_bytes(T_BYTE);
if ((uint)off >= (uint)min_base_off) { // is the offset beyond the header?
const Type* jt = t->join(_type); const Type* jt = t->join(_type);
// In any case, do not allow the join, per se, to empty out the type. // In any case, do not allow the join, per se, to empty out the type.
if (jt->empty() && !t->empty()) { if (jt->empty() && !t->empty()) {
......
...@@ -518,7 +518,7 @@ class Parse : public GraphKit { ...@@ -518,7 +518,7 @@ class Parse : public GraphKit {
// loading from a constant field or the constant pool // loading from a constant field or the constant pool
// returns false if push failed (non-perm field constants only, not ldcs) // returns false if push failed (non-perm field constants only, not ldcs)
bool push_constant(ciConstant con, bool require_constant = false, bool is_autobox_cache = false); bool push_constant(ciConstant con, bool require_constant = false, bool is_autobox_cache = false, const Type* basic_type = NULL);
// implementation of object creation bytecodes // implementation of object creation bytecodes
void emit_guard_for_new(ciInstanceKlass* klass); void emit_guard_for_new(ciInstanceKlass* klass);
......
...@@ -147,7 +147,15 @@ void Parse::do_field_access(bool is_get, bool is_field) { ...@@ -147,7 +147,15 @@ void Parse::do_field_access(bool is_get, bool is_field) {
void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) { void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
// Does this field have a constant value? If so, just push the value. // Does this field have a constant value? If so, just push the value.
if (field->is_constant()) { if (field->is_constant()) {
// final field // final or stable field
const Type* stable_type = NULL;
if (FoldStableValues && field->is_stable()) {
stable_type = Type::get_const_type(field->type());
if (field->type()->is_array_klass()) {
int stable_dimension = field->type()->as_array_klass()->dimension();
stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension);
}
}
if (field->is_static()) { if (field->is_static()) {
// final static field // final static field
if (C->eliminate_boxing()) { if (C->eliminate_boxing()) {
...@@ -167,11 +175,10 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) { ...@@ -167,11 +175,10 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
} }
} }
} }
if (push_constant(field->constant_value())) if (push_constant(field->constant_value(), false, false, stable_type))
return; return;
} } else {
else { // final or stable non-static field
// final non-static field
// Treat final non-static fields of trusted classes (classes in // Treat final non-static fields of trusted classes (classes in
// java.lang.invoke and sun.invoke packages and subpackages) as // java.lang.invoke and sun.invoke packages and subpackages) as
// compile time constants. // compile time constants.
...@@ -179,8 +186,12 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) { ...@@ -179,8 +186,12 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr(); const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr();
ciObject* constant_oop = oop_ptr->const_oop(); ciObject* constant_oop = oop_ptr->const_oop();
ciConstant constant = field->constant_value_of(constant_oop); ciConstant constant = field->constant_value_of(constant_oop);
if (push_constant(constant, true)) if (FoldStableValues && field->is_stable() && constant.is_null_or_zero()) {
return; // fall through to field load; the field is not yet initialized
} else {
if (push_constant(constant, true, false, stable_type))
return;
}
} }
} }
} }
...@@ -301,7 +312,8 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) { ...@@ -301,7 +312,8 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
// Note the presence of writes to final non-static fields, so that we // Note the presence of writes to final non-static fields, so that we
// can insert a memory barrier later on to keep the writes from floating // can insert a memory barrier later on to keep the writes from floating
// out of the constructor. // out of the constructor.
if (is_field && field->is_final()) { // Any method can write a @Stable field; insert memory barriers after those also.
if (is_field && (field->is_final() || field->is_stable())) {
set_wrote_final(true); set_wrote_final(true);
// Preserve allocation ptr to create precedent edge to it in membar // Preserve allocation ptr to create precedent edge to it in membar
// generated on exit from constructor. // generated on exit from constructor.
...@@ -314,35 +326,21 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) { ...@@ -314,35 +326,21 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
} }
bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache) {
bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache, const Type* stable_type) {
const Type* con_type = Type::make_from_constant(constant, require_constant, is_autobox_cache);
switch (constant.basic_type()) { switch (constant.basic_type()) {
case T_BOOLEAN: push( intcon(constant.as_boolean()) ); break;
case T_INT: push( intcon(constant.as_int()) ); break;
case T_CHAR: push( intcon(constant.as_char()) ); break;
case T_BYTE: push( intcon(constant.as_byte()) ); break;
case T_SHORT: push( intcon(constant.as_short()) ); break;
case T_FLOAT: push( makecon(TypeF::make(constant.as_float())) ); break;
case T_DOUBLE: push_pair( makecon(TypeD::make(constant.as_double())) ); break;
case T_LONG: push_pair( longcon(constant.as_long()) ); break;
case T_ARRAY: case T_ARRAY:
case T_OBJECT: { case T_OBJECT:
// cases: // cases:
// can_be_constant = (oop not scavengable || ScavengeRootsInCode != 0) // can_be_constant = (oop not scavengable || ScavengeRootsInCode != 0)
// should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2) // should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2)
// An oop is not scavengable if it is in the perm gen. // An oop is not scavengable if it is in the perm gen.
ciObject* oop_constant = constant.as_object(); if (stable_type != NULL && con_type != NULL && con_type->isa_oopptr())
if (oop_constant->is_null_object()) { con_type = con_type->join(stable_type);
push( zerocon(T_OBJECT) ); break;
break;
} else if (require_constant || oop_constant->should_be_constant()) { case T_ILLEGAL:
push( makecon(TypeOopPtr::make_from_constant(oop_constant, require_constant, is_autobox_cache)) );
break;
} else {
// we cannot inline the oop, but we can use it later to narrow a type
return false;
}
}
case T_ILLEGAL: {
// Invalid ciConstant returned due to OutOfMemoryError in the CI // Invalid ciConstant returned due to OutOfMemoryError in the CI
assert(C->env()->failing(), "otherwise should not see this"); assert(C->env()->failing(), "otherwise should not see this");
// These always occur because of object types; we are going to // These always occur because of object types; we are going to
...@@ -350,17 +348,16 @@ bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_au ...@@ -350,17 +348,16 @@ bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_au
push( zerocon(T_OBJECT) ); push( zerocon(T_OBJECT) );
return false; return false;
} }
default:
ShouldNotReachHere(); if (con_type == NULL)
// we cannot inline the oop, but we can use it later to narrow a type
return false; return false;
}
// success push_node(constant.basic_type(), makecon(con_type));
return true; return true;
} }
//============================================================================= //=============================================================================
void Parse::do_anewarray() { void Parse::do_anewarray() {
bool will_link; bool will_link;
......
...@@ -189,6 +189,38 @@ const Type* Type::get_typeflow_type(ciType* type) { ...@@ -189,6 +189,38 @@ const Type* Type::get_typeflow_type(ciType* type) {
} }
//-----------------------make_from_constant------------------------------------
const Type* Type::make_from_constant(ciConstant constant,
bool require_constant, bool is_autobox_cache) {
switch (constant.basic_type()) {
case T_BOOLEAN: return TypeInt::make(constant.as_boolean());
case T_CHAR: return TypeInt::make(constant.as_char());
case T_BYTE: return TypeInt::make(constant.as_byte());
case T_SHORT: return TypeInt::make(constant.as_short());
case T_INT: return TypeInt::make(constant.as_int());
case T_LONG: return TypeLong::make(constant.as_long());
case T_FLOAT: return TypeF::make(constant.as_float());
case T_DOUBLE: return TypeD::make(constant.as_double());
case T_ARRAY:
case T_OBJECT:
{
// cases:
// can_be_constant = (oop not scavengable || ScavengeRootsInCode != 0)
// should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2)
// An oop is not scavengable if it is in the perm gen.
ciObject* oop_constant = constant.as_object();
if (oop_constant->is_null_object()) {
return Type::get_zero_type(T_OBJECT);
} else if (require_constant || oop_constant->should_be_constant()) {
return TypeOopPtr::make_from_constant(oop_constant, require_constant, is_autobox_cache);
}
}
}
// Fall through to failure
return NULL;
}
//------------------------------make------------------------------------------- //------------------------------make-------------------------------------------
// Create a simple Type, with default empty symbol sets. Then hashcons it // Create a simple Type, with default empty symbol sets. Then hashcons it
// and look for an existing copy in the type dictionary. // and look for an existing copy in the type dictionary.
...@@ -1824,12 +1856,12 @@ inline const TypeInt* normalize_array_size(const TypeInt* size) { ...@@ -1824,12 +1856,12 @@ inline const TypeInt* normalize_array_size(const TypeInt* size) {
} }
//------------------------------make------------------------------------------- //------------------------------make-------------------------------------------
const TypeAry *TypeAry::make( const Type *elem, const TypeInt *size) { const TypeAry* TypeAry::make(const Type* elem, const TypeInt* size, bool stable) {
if (UseCompressedOops && elem->isa_oopptr()) { if (UseCompressedOops && elem->isa_oopptr()) {
elem = elem->make_narrowoop(); elem = elem->make_narrowoop();
} }
size = normalize_array_size(size); size = normalize_array_size(size);
return (TypeAry*)(new TypeAry(elem,size))->hashcons(); return (TypeAry*)(new TypeAry(elem,size,stable))->hashcons();
} }
//------------------------------meet------------------------------------------- //------------------------------meet-------------------------------------------
...@@ -1850,7 +1882,8 @@ const Type *TypeAry::xmeet( const Type *t ) const { ...@@ -1850,7 +1882,8 @@ const Type *TypeAry::xmeet( const Type *t ) const {
case Array: { // Meeting 2 arrays? case Array: { // Meeting 2 arrays?
const TypeAry *a = t->is_ary(); const TypeAry *a = t->is_ary();
return TypeAry::make(_elem->meet(a->_elem), return TypeAry::make(_elem->meet(a->_elem),
_size->xmeet(a->_size)->is_int()); _size->xmeet(a->_size)->is_int(),
_stable & a->_stable);
} }
case Top: case Top:
break; break;
...@@ -1863,7 +1896,7 @@ const Type *TypeAry::xmeet( const Type *t ) const { ...@@ -1863,7 +1896,7 @@ const Type *TypeAry::xmeet( const Type *t ) const {
const Type *TypeAry::xdual() const { const Type *TypeAry::xdual() const {
const TypeInt* size_dual = _size->dual()->is_int(); const TypeInt* size_dual = _size->dual()->is_int();
size_dual = normalize_array_size(size_dual); size_dual = normalize_array_size(size_dual);
return new TypeAry( _elem->dual(), size_dual); return new TypeAry(_elem->dual(), size_dual, !_stable);
} }
//------------------------------eq--------------------------------------------- //------------------------------eq---------------------------------------------
...@@ -1871,13 +1904,14 @@ const Type *TypeAry::xdual() const { ...@@ -1871,13 +1904,14 @@ const Type *TypeAry::xdual() const {
bool TypeAry::eq( const Type *t ) const { bool TypeAry::eq( const Type *t ) const {
const TypeAry *a = (const TypeAry*)t; const TypeAry *a = (const TypeAry*)t;
return _elem == a->_elem && return _elem == a->_elem &&
_stable == a->_stable &&
_size == a->_size; _size == a->_size;
} }
//------------------------------hash------------------------------------------- //------------------------------hash-------------------------------------------
// Type-specific hashing function. // Type-specific hashing function.
int TypeAry::hash(void) const { int TypeAry::hash(void) const {
return (intptr_t)_elem + (intptr_t)_size; return (intptr_t)_elem + (intptr_t)_size + (_stable ? 43 : 0);
} }
//----------------------interface_vs_oop--------------------------------------- //----------------------interface_vs_oop---------------------------------------
...@@ -1894,6 +1928,7 @@ bool TypeAry::interface_vs_oop(const Type *t) const { ...@@ -1894,6 +1928,7 @@ bool TypeAry::interface_vs_oop(const Type *t) const {
//------------------------------dump2------------------------------------------ //------------------------------dump2------------------------------------------
#ifndef PRODUCT #ifndef PRODUCT
void TypeAry::dump2( Dict &d, uint depth, outputStream *st ) const { void TypeAry::dump2( Dict &d, uint depth, outputStream *st ) const {
if (_stable) st->print("stable:");
_elem->dump2(d, depth, st); _elem->dump2(d, depth, st);
st->print("["); st->print("[");
_size->dump2(d, depth, st); _size->dump2(d, depth, st);
...@@ -3457,11 +3492,39 @@ const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const { ...@@ -3457,11 +3492,39 @@ const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const {
assert(new_size != NULL, ""); assert(new_size != NULL, "");
new_size = narrow_size_type(new_size); new_size = narrow_size_type(new_size);
if (new_size == size()) return this; if (new_size == size()) return this;
const TypeAry* new_ary = TypeAry::make(elem(), new_size); const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable());
return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id); return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id);
} }
//------------------------------cast_to_stable---------------------------------
const TypeAryPtr* TypeAryPtr::cast_to_stable(bool stable, int stable_dimension) const {
if (stable_dimension <= 0 || (stable_dimension == 1 && stable == this->is_stable()))
return this;
const Type* elem = this->elem();
const TypePtr* elem_ptr = elem->make_ptr();
if (stable_dimension > 1 && elem_ptr != NULL && elem_ptr->isa_aryptr()) {
// If this is widened from a narrow oop, TypeAry::make will re-narrow it.
elem = elem_ptr = elem_ptr->is_aryptr()->cast_to_stable(stable, stable_dimension - 1);
}
const TypeAry* new_ary = TypeAry::make(elem, size(), stable);
return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id);
}
//-----------------------------stable_dimension--------------------------------
int TypeAryPtr::stable_dimension() const {
if (!is_stable()) return 0;
int dim = 1;
const TypePtr* elem_ptr = elem()->make_ptr();
if (elem_ptr != NULL && elem_ptr->isa_aryptr())
dim += elem_ptr->is_aryptr()->stable_dimension();
return dim;
}
//------------------------------eq--------------------------------------------- //------------------------------eq---------------------------------------------
// Structural equality check for Type representations // Structural equality check for Type representations
bool TypeAryPtr::eq( const Type *t ) const { bool TypeAryPtr::eq( const Type *t ) const {
...@@ -3570,7 +3633,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const { ...@@ -3570,7 +3633,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const {
// Something like byte[int+] meets char[int+]. // Something like byte[int+] meets char[int+].
// This must fall to bottom, not (int[-128..65535])[int+]. // This must fall to bottom, not (int[-128..65535])[int+].
instance_id = InstanceBot; instance_id = InstanceBot;
tary = TypeAry::make(Type::BOTTOM, tary->_size); tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable);
} }
} else // Non integral arrays. } else // Non integral arrays.
// Must fall to bottom if exact klasses in upper lattice // Must fall to bottom if exact klasses in upper lattice
...@@ -3584,7 +3647,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const { ...@@ -3584,7 +3647,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const {
(tap ->_klass_is_exact && !tap->klass()->is_subtype_of(klass())) || (tap ->_klass_is_exact && !tap->klass()->is_subtype_of(klass())) ||
// 'this' is exact and super or unrelated: // 'this' is exact and super or unrelated:
(this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) { (this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) {
tary = TypeAry::make(Type::BOTTOM, tary->_size); tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable);
return make( NotNull, NULL, tary, lazy_klass, false, off, InstanceBot ); return make( NotNull, NULL, tary, lazy_klass, false, off, InstanceBot );
} }
......
...@@ -372,6 +372,10 @@ public: ...@@ -372,6 +372,10 @@ public:
// Mapping from CI type system to compiler type: // Mapping from CI type system to compiler type:
static const Type* get_typeflow_type(ciType* type); static const Type* get_typeflow_type(ciType* type);
static const Type* make_from_constant(ciConstant constant,
bool require_constant = false,
bool is_autobox_cache = false);
private: private:
// support arrays // support arrays
static const BasicType _basic_type[]; static const BasicType _basic_type[];
...@@ -588,8 +592,8 @@ public: ...@@ -588,8 +592,8 @@ public:
//------------------------------TypeAry---------------------------------------- //------------------------------TypeAry----------------------------------------
// Class of Array Types // Class of Array Types
class TypeAry : public Type { class TypeAry : public Type {
TypeAry( const Type *elem, const TypeInt *size) : Type(Array), TypeAry(const Type* elem, const TypeInt* size, bool stable) : Type(Array),
_elem(elem), _size(size) {} _elem(elem), _size(size), _stable(stable) {}
public: public:
virtual bool eq( const Type *t ) const; virtual bool eq( const Type *t ) const;
virtual int hash() const; // Type specific hashing virtual int hash() const; // Type specific hashing
...@@ -599,10 +603,11 @@ public: ...@@ -599,10 +603,11 @@ public:
private: private:
const Type *_elem; // Element type of array const Type *_elem; // Element type of array
const TypeInt *_size; // Elements in array const TypeInt *_size; // Elements in array
const bool _stable; // Are elements @Stable?
friend class TypeAryPtr; friend class TypeAryPtr;
public: public:
static const TypeAry *make( const Type *elem, const TypeInt *size); static const TypeAry* make(const Type* elem, const TypeInt* size, bool stable = false);
virtual const Type *xmeet( const Type *t ) const; virtual const Type *xmeet( const Type *t ) const;
virtual const Type *xdual() const; // Compute dual right now. virtual const Type *xdual() const; // Compute dual right now.
...@@ -988,6 +993,7 @@ public: ...@@ -988,6 +993,7 @@ public:
const TypeAry* ary() const { return _ary; } const TypeAry* ary() const { return _ary; }
const Type* elem() const { return _ary->_elem; } const Type* elem() const { return _ary->_elem; }
const TypeInt* size() const { return _ary->_size; } const TypeInt* size() const { return _ary->_size; }
bool is_stable() const { return _ary->_stable; }
bool is_autobox_cache() const { return _is_autobox_cache; } bool is_autobox_cache() const { return _is_autobox_cache; }
...@@ -1011,6 +1017,9 @@ public: ...@@ -1011,6 +1017,9 @@ public:
virtual const Type *xmeet( const Type *t ) const; virtual const Type *xmeet( const Type *t ) const;
virtual const Type *xdual() const; // Compute dual right now. virtual const Type *xdual() const; // Compute dual right now.
const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const;
int stable_dimension() const;
// Convenience common pre-built types. // Convenience common pre-built types.
static const TypeAryPtr *RANGE; static const TypeAryPtr *RANGE;
static const TypeAryPtr *OOPS; static const TypeAryPtr *OOPS;
......
...@@ -3649,6 +3649,9 @@ class CommandLineFlags { ...@@ -3649,6 +3649,9 @@ class CommandLineFlags {
experimental(bool, TrustFinalNonStaticFields, false, \ experimental(bool, TrustFinalNonStaticFields, false, \
"trust final non-static declarations for constant folding") \ "trust final non-static declarations for constant folding") \
\ \
experimental(bool, FoldStableValues, false, \
"Private flag to control optimizations for stable variables") \
\
develop(bool, TraceInvokeDynamic, false, \ develop(bool, TraceInvokeDynamic, false, \
"trace internal invoke dynamic operations") \ "trace internal invoke dynamic operations") \
\ \
......
...@@ -471,16 +471,6 @@ class RuntimeHistogramElement : public HistogramElement { ...@@ -471,16 +471,6 @@ class RuntimeHistogramElement : public HistogramElement {
VM_ENTRY_BASE(result_type, header, thread) \ VM_ENTRY_BASE(result_type, header, thread) \
debug_only(VMEntryWrapper __vew;) debug_only(VMEntryWrapper __vew;)
// Another special case for nmethod_entry_point so the nmethod that the
// interpreter is about to branch to doesn't get flushed before as we
// branch to it's interpreter_entry_point. Skip stress testing here too.
// Also we don't allow async exceptions because it is just too painful.
#define IRT_ENTRY_FOR_NMETHOD(result_type, header) \
result_type header { \
nmethodLocker _nmlock(nm); \
ThreadInVMfromJavaNoAsyncException __tiv(thread); \
VM_ENTRY_BASE(result_type, header, thread)
#define IRT_END } #define IRT_END }
......
...@@ -78,11 +78,13 @@ enum { ...@@ -78,11 +78,13 @@ enum {
JVM_ACC_FIELD_ACCESS_WATCHED = 0x00002000, // field access is watched by JVMTI JVM_ACC_FIELD_ACCESS_WATCHED = 0x00002000, // field access is watched by JVMTI
JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x00008000, // field modification is watched by JVMTI JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x00008000, // field modification is watched by JVMTI
JVM_ACC_FIELD_INTERNAL = 0x00000400, // internal field, same as JVM_ACC_ABSTRACT JVM_ACC_FIELD_INTERNAL = 0x00000400, // internal field, same as JVM_ACC_ABSTRACT
JVM_ACC_FIELD_STABLE = 0x00000020, // @Stable field, same as JVM_ACC_SYNCHRONIZED
JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE = 0x00000800, // field has generic signature JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE = 0x00000800, // field has generic signature
JVM_ACC_FIELD_INTERNAL_FLAGS = JVM_ACC_FIELD_ACCESS_WATCHED | JVM_ACC_FIELD_INTERNAL_FLAGS = JVM_ACC_FIELD_ACCESS_WATCHED |
JVM_ACC_FIELD_MODIFICATION_WATCHED | JVM_ACC_FIELD_MODIFICATION_WATCHED |
JVM_ACC_FIELD_INTERNAL | JVM_ACC_FIELD_INTERNAL |
JVM_ACC_FIELD_STABLE |
JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE, JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE,
// flags accepted by set_field_flags() // flags accepted by set_field_flags()
...@@ -148,6 +150,7 @@ class AccessFlags VALUE_OBJ_CLASS_SPEC { ...@@ -148,6 +150,7 @@ class AccessFlags VALUE_OBJ_CLASS_SPEC {
{ return (_flags & JVM_ACC_FIELD_MODIFICATION_WATCHED) != 0; } { return (_flags & JVM_ACC_FIELD_MODIFICATION_WATCHED) != 0; }
bool on_stack() const { return (_flags & JVM_ACC_ON_STACK) != 0; } bool on_stack() const { return (_flags & JVM_ACC_ON_STACK) != 0; }
bool is_internal() const { return (_flags & JVM_ACC_FIELD_INTERNAL) != 0; } bool is_internal() const { return (_flags & JVM_ACC_FIELD_INTERNAL) != 0; }
bool is_stable() const { return (_flags & JVM_ACC_FIELD_STABLE) != 0; }
bool field_has_generic_signature() const bool field_has_generic_signature() const
{ return (_flags & JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE) != 0; } { return (_flags & JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE) != 0; }
......
...@@ -48,7 +48,7 @@ public class TestVerifyDuringStartup { ...@@ -48,7 +48,7 @@ public class TestVerifyDuringStartup {
"-XX:+VerifyDuringStartup", "-XX:+VerifyDuringStartup",
"-version"}); "-version"});
System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java")); System.out.print("Testing:\n" + JDKToolFinder.getCurrentJDKTool("java"));
for (int i = 0; i < vmOpts.size(); i += 1) { for (int i = 0; i < vmOpts.size(); i += 1) {
System.out.print(" " + vmOpts.get(i)); System.out.print(" " + vmOpts.get(i));
} }
......
...@@ -27,24 +27,43 @@ import java.io.File; ...@@ -27,24 +27,43 @@ import java.io.File;
public final class JDKToolFinder { public final class JDKToolFinder {
private JDKToolFinder() { private JDKToolFinder() {
}
/**
* Returns the full path to an executable in jdk/bin based on System property
* test.jdk (set by jtreg test suite)
*
* @return Full path to an executable in jdk/bin
*/
public static String getJDKTool(String tool) {
String binPath = System.getProperty("test.jdk");
if (binPath == null) {
throw new RuntimeException("System property 'test.jdk' not set. This property is normally set by jtreg. "
+ "When running test separately, set this property using '-Dtest.jdk=/path/to/jdk'.");
} }
binPath += File.separatorChar + "bin" + File.separatorChar + tool; /**
* Returns the full path to an executable in jdk/bin based on System
* property {@code compile.jdk} (set by jtreg test suite)
*
* @return Full path to an executable in jdk/bin
*/
public static String getJDKTool(String tool) {
String binPath = System.getProperty("compile.jdk");
if (binPath == null) {
throw new RuntimeException("System property 'compile.jdk' not set. "
+ "This property is normally set by jtreg. "
+ "When running test separately, set this property using "
+ "'-Dcompile.jdk=/path/to/jdk'.");
}
binPath += File.separatorChar + "bin" + File.separatorChar + tool;
return binPath;
}
/**
* Returns the full path to an executable in &lt;current jdk&gt;/bin based
* on System property {@code test.jdk} (set by jtreg test suite)
*
* @return Full path to an executable in jdk/bin
*/
public static String getCurrentJDKTool(String tool) {
String binPath = System.getProperty("test.jdk");
if (binPath == null) {
throw new RuntimeException("System property 'test.jdk' not set. "
+ "This property is normally set by jtreg. "
+ "When running test separately, set this property using "
+ "'-Dtest.jdk=/path/to/jdk'.");
}
binPath += File.separatorChar + "bin" + File.separatorChar + tool;
return binPath; return binPath;
} }
} }
#
# 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.
#
#
ifneq "x$(ALT_BOOTDIR)" "x"
BOOTDIR := $(ALT_BOOTDIR)
endif
ifeq "x$(BOOTDIR)" "x"
JDK_HOME := $(shell dirname $(shell which java))/..
else
JDK_HOME := $(BOOTDIR)
endif
SRC_DIR = src
BUILD_DIR = build
OUTPUT_DIR = $(BUILD_DIR)/classes
WHITEBOX_DIR = ../whitebox
JAVAC = $(JDK_HOME)/bin/javac
JAR = $(JDK_HOME)/bin/jar
SRC_FILES = $(shell find $(SRC_DIR) -name '*.java')
MAIN_CLASS = sun.hotspot.tools.ctw.CompileTheWorld
.PHONY: clean cleantmp
all: ctw.jar cleantmp
clean: cleantmp
@rm -rf ctw.jar wb.jar
cleantmp:
@rm -rf filelist manifest.mf
@rm -rf $(BUILD_DIR)
ctw.jar: filelist wb.jar manifest.mf
@mkdir -p $(OUTPUT_DIR)
$(JAVAC) -sourcepath $(SRC_DIR) -d $(OUTPUT_DIR) -cp wb.jar @filelist
$(JAR) cfm ctw.jar manifest.mf -C $(OUTPUT_DIR) .
wb.jar:
make -C ${WHITEBOX_DIR} wb.jar
cp ${WHITEBOX_DIR}/wb.jar ./
make -C ${WHITEBOX_DIR} clean
filelist: $(SRC_FILES)
@rm -f $@
@echo $(SRC_FILES) > $@
manifest.mf:
@echo "Main-Class: ${MAIN_CLASS}" > manifest.mf
#
# 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.
#
#
DESCRIPTION
This is replacement for CompileTheWorld (CTW) written on java. Its purpose is
to make possible the use of CTW in product builds.
DEPENDENCES
The tool depends on Whitebox API. Assumed, that the sources of whitebox are
located in '../whitebox' directory.
BUILDING
Simple way to build, just type 'make'.
Makefile uses environment variables 'ALT_BOOTDIR', 'BOOTDIR' as root-dir of jdk
that will be used for compilation and creating jar.
On successful building 'ctw.jar' will be created.
RUNNING
Since the tool uses WhiteBox API, options 'UnlockDiagnosticVMOptions' and
'WhiteBoxAPI' should be specified, and 'wb.jar' should be added to
boot-classpath:
$ java -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:wb.jar -jar ctw.jar
Arguments can be paths to '.jar, '.zip', '.lst' files or directories with
classes, that define which classes will be compiled:
- '.jar', '.zip' files and directories are interpreted like in classpath
(including '<dir>/*' syntax)
- '.lst' files -- files with class names (in java notation) to compile.
CTW will try to find these classes with default class loader, so they should
be located in classpath.
Without arguments it would work as old version of CTW: all classes in
boot-classpath will be compiled, excluding classes in 'rt.jar' if 'rt.jar' isn't
first in boot-classpath.
Due CTW's flags also are not available in product builds, the tool uses
properties with the same names:
- 'CompileTheWorldPreloadClasses' -- type:boolean, default:true, description:
Preload all classes used by a class before start loading
- 'CompileTheWorldStartAt' -- type:long, default:1, description: First class
to consider
- 'CompileTheWorldStopAt' -- type:long, default:Long.MAX_VALUE, description:
Last class to consider
Also it uses additional properties:
- 'sun.hotspot.tools.ctw.verbose' -- type:boolean, default:false,
description: Verbose output, adds additional information about compilation
- 'sun.hotspot.tools.ctw.logfile' -- type:string, default:null,
description: Path to logfile, if it's null, cout will be used.
EXAMPLES
compile classes from 'rt.jar':
$ java -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:wb.jar -jar ctw.jar ${JAVA_HOME}/jre/lib/rt.jar
compile classes from all '.jar' in './testjars' directory:
$ java -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:wb.jar -jar ctw.jar ./testjars/*
compile classes from './build/classes' directory:
$ java -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:wb.jar -jar ctw.jar ./build/classes
compile only java.lang.String, java.lang.Object classes:
$ echo java.lang.String > classes.lst
$ echo java.lang.Object >> classes.lst
$ java -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:wb.jar -jar ctw.jar classes.lst
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Set;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.concurrent.Executor;
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
/**
* * Handler for dirs containing classes to compile.
* @author igor.ignatyev@oracle.com
*/
public class ClassPathDirEntry extends PathHandler {
private final int rootLength = root.toString().length();
public ClassPathDirEntry(Path root, Executor executor) {
super(root, executor);
try {
URL url = root.toUri().toURL();
setLoader(new URLClassLoader(new URL[]{url}));
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
@Override
public void process() {
System.out.println("# dir: " + root);
if (!Files.exists(root)) {
return;
}
try {
Files.walkFileTree(root, EnumSet.of(FileVisitOption.FOLLOW_LINKS),
Integer.MAX_VALUE, new CompileFileVisitor());
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
private void processFile(Path file) {
if (Utils.isClassFile(file.toString())) {
processClass(pathToClassName(file));
}
}
private String pathToClassName(Path file) {
String fileString;
if (root == file) {
fileString = file.normalize().toString();
} else {
fileString = file.normalize().toString().substring(rootLength + 1);
}
return Utils.fileNameToClassName(fileString);
}
private class CompileFileVisitor extends SimpleFileVisitor<Path> {
private final Set<Path> ready = new HashSet<>();
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
if (ready.contains(dir)) {
return FileVisitResult.SKIP_SUBTREE;
}
ready.add(dir);
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
if (!ready.contains(file)) {
processFile(file);
}
return isFinished() ? FileVisitResult.TERMINATE
: FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file,
IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.*;
import java.util.concurrent.Executor;
import java.io.*;
import java.nio.file.*;
/**
* Handler for jar-files containing classes to compile.
* @author igor.ignatyev@oracle.com
*/
public class ClassPathJarEntry extends PathHandler {
public ClassPathJarEntry(Path root, Executor executor) {
super(root, executor);
try {
URL url = root.toUri().toURL();
setLoader(new URLClassLoader(new URL[]{url}));
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
@Override
public void process() {
System.out.println("# jar: " + root);
if (!Files.exists(root)) {
return;
}
try {
JarFile jarFile = new JarFile(root.toFile());
JarEntry entry;
for (Enumeration<JarEntry> e = jarFile.entries();
e.hasMoreElements(); ) {
entry = e.nextElement();
processJarEntry(entry);
if (isFinished()) {
return;
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
private void processJarEntry(JarEntry entry) {
String filename = entry.getName();
if (Utils.isClassFile(filename)) {
processClass(Utils.fileNameToClassName(filename));
}
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Executor;
/**
* Handler for dirs containing jar-files with classes to compile.
*
* @author igor.ignatyev@oracle.com
*/
public class ClassPathJarInDirEntry extends PathHandler {
public ClassPathJarInDirEntry(Path root, Executor executor) {
super(root, executor);
}
@Override
public void process() {
System.out.println("# jar_in_dir: " + root);
if (!Files.exists(root)) {
return;
}
try (DirectoryStream<Path> ds
= Files.newDirectoryStream(root, "*.jar")) {
for (Path p : ds) {
new ClassPathJarEntry(p, executor).process();
if (isFinished()) {
return;
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Executor;
/**
* Handler for files containing a list of classes to compile.
*
* @author igor.ignatyev@oracle.com
*/
public class ClassesListInFile extends PathHandler {
public ClassesListInFile(Path root, Executor executor) {
super(root, executor);
}
@Override
public void process() {
System.out.println("# list: " + root);
if (!Files.exists(root)) {
return;
}
try {
try (BufferedReader reader = Files.newBufferedReader(root,
StandardCharsets.UTF_8)) {
String line;
while (!isFinished() && ((line = reader.readLine()) != null)) {
processClass(line);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import sun.management.ManagementFactoryHelper;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.*;
/**
* @author igor.ignatyev@oracle.com
*/
public class CompileTheWorld {
/**
* Entry point. Compiles classes in {@code args}, or all classes in
* boot-classpath if args is empty
*
* @param args paths to jar/zip, dir contains classes, or to .lst file
* contains list of classes to compile
*/
public static void main(String[] args) {
String logfile = Utils.LOG_FILE;
PrintStream os = null;
if (logfile != null) {
try {
os = new PrintStream(Files.newOutputStream(Paths.get(logfile)));
} catch (IOException io) {
}
}
if (os != null) {
System.setOut(os);
}
try {
try {
if (ManagementFactoryHelper.getCompilationMXBean() == null) {
throw new RuntimeException(
"CTW can not work in interpreted mode");
}
} catch (java.lang.NoClassDefFoundError e) {
// compact1, compact2 support
}
String[] paths = args;
boolean skipRtJar = false;
if (args.length == 0) {
paths = getDefaultPaths();
skipRtJar = true;
}
ExecutorService executor = createExecutor();
long start = System.currentTimeMillis();
try {
String path;
for (int i = 0, n = paths.length; i < n
&& !PathHandler.isFinished(); ++i) {
path = paths[i];
if (skipRtJar && i > 0 && isRtJar(path)) {
// rt.jar is not first, so skip it
continue;
}
PathHandler.create(path, executor).process();
}
} finally {
await(executor);
}
System.out.printf("Done (%d classes, %d methods, %d ms)%n",
Compiler.getClassCount(),
Compiler.getMethodCount(),
System.currentTimeMillis() - start);
} finally {
if (os != null) {
os.close();
}
}
}
private static ExecutorService createExecutor() {
final int threadsCount = Math.min(
Runtime.getRuntime().availableProcessors(),
Utils.CI_COMPILER_COUNT);
ExecutorService result;
if (threadsCount > 1) {
result = new ThreadPoolExecutor(threadsCount, threadsCount,
/* keepAliveTime */ 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(threadsCount),
new ThreadPoolExecutor.CallerRunsPolicy());
} else {
result = new CurrentThreadExecutor();
}
return result;
}
private static String[] getDefaultPaths() {
String property = System.getProperty("sun.boot.class.path");
System.out.println(
"# use 'sun.boot.class.path' as args: " + property);
return Utils.PATH_SEPARATOR.split(property);
}
private static void await(ExecutorService executor) {
executor.shutdown();
while (!executor.isTerminated()) {
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
private static boolean isRtJar(String path) {
return Utils.endsWithIgnoreCase(path, File.separator + "rt.jar");
}
private static class CurrentThreadExecutor extends AbstractExecutorService {
private boolean isShutdown;
@Override
public void shutdown() {
this.isShutdown = true;
}
@Override
public List<Runnable> shutdownNow() {
return null;
}
@Override
public boolean isShutdown() {
return isShutdown;
}
@Override
public boolean isTerminated() {
return isShutdown;
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return isShutdown;
}
@Override
public void execute(Runnable command) {
command.run();
}
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import sun.hotspot.WhiteBox;
import sun.misc.SharedSecrets;
import sun.reflect.ConstantPool;
import java.lang.reflect.Executable;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
/**
* Provide method to compile whole class.
* Also contains compiled methods and classes counters.
*
* @author igor.ignatyev@oracle.com
*/
public class Compiler {
private Compiler() { }
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
private static final AtomicLong CLASS_COUNT = new AtomicLong(0L);
private static final AtomicLong METHOD_COUNT = new AtomicLong(0L);
private static volatile boolean CLASSES_LIMIT_REACHED = false;
/**
* @return count of processed classes
*/
public static long getClassCount() {
return CLASS_COUNT.get();
}
/**
* @return count of processed methods
*/
public static long getMethodCount() {
return METHOD_COUNT.get();
}
/**
* @return {@code true} if classes limit is reached
*/
public static boolean isLimitReached() {
return CLASSES_LIMIT_REACHED;
}
/**
* Compiles all methods and constructors.
*
* @param aClass class to compile
* @param executor executor used for compile task invocation
* @throws NullPointerException if {@code class} or {@code executor}
* is {@code null}
*/
public static void compileClass(Class aClass, Executor executor) {
Objects.requireNonNull(aClass);
Objects.requireNonNull(executor);
long id = CLASS_COUNT.incrementAndGet();
if (id > Utils.COMPILE_THE_WORLD_STOP_AT) {
CLASS_COUNT.decrementAndGet();
CLASSES_LIMIT_REACHED = true;
return;
}
if (id >= Utils.COMPILE_THE_WORLD_START_AT) {
String name = aClass.getName();
try {
System.out.printf("[%d]\t%s%n", id, name);
ConstantPool constantPool = SharedSecrets.getJavaLangAccess().
getConstantPool(aClass);
if (Utils.COMPILE_THE_WORLD_PRELOAD_CLASSES) {
preloadClasses(name, id, constantPool);
}
long methodCount = 0;
for (Executable e : aClass.getDeclaredConstructors()) {
++methodCount;
executor.execute(new CompileMethodCommand(id, name, e));
}
for (Executable e : aClass.getDeclaredMethods()) {
++methodCount;
executor.execute(new CompileMethodCommand(id, name, e));
}
METHOD_COUNT.addAndGet(methodCount);
if (Utils.DEOPTIMIZE_ALL_CLASSES_RATE > 0
&& (id % Utils.DEOPTIMIZE_ALL_CLASSES_RATE == 0)) {
WHITE_BOX.deoptimizeAll();
}
} catch (Throwable t) {
System.out.printf("[%d]\t%s\tskipping %s%n", id, name, t);
t.printStackTrace();
}
}
}
private static void preloadClasses(String className, long id,
ConstantPool constantPool) {
try {
for (int i = 0, n = constantPool.getSize(); i < n; ++i) {
try {
constantPool.getClassAt(i);
} catch (IllegalArgumentException ignore) {
}
}
} catch (Throwable t) {
System.out.printf("[%d]\t%s\tpreloading failed : %s%n", id,
className, t);
}
}
/**
* Compilation of method.
* Will compile method on all available comp levels.
*/
private static class CompileMethodCommand implements Runnable {
private final long classId;
private final String className;
private final Executable method;
/**
* @param classId id of class
* @param className name of class
* @param method compiled for compilation
*/
public CompileMethodCommand(long classId, String className,
Executable method) {
this.classId = classId;
this.className = className;
this.method = method;
}
@Override
public final void run() {
int compLevel = Utils.INITIAL_COMP_LEVEL;
if (Utils.TIERED_COMPILATION) {
for (int i = compLevel; i <= Utils.TIERED_STOP_AT_LEVEL; ++i) {
WHITE_BOX.deoptimizeMethod(method);
compileMethod(method, i);
}
} else {
compileMethod(method, compLevel);
}
}
private void waitCompilation() {
if (!Utils.BACKGROUND_COMPILATION) {
return;
}
final Object obj = new Object();
synchronized (obj) {
for (int i = 0;
i < 10 && WHITE_BOX.isMethodQueuedForCompilation(method);
++i) {
try {
obj.wait(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
private void compileMethod(Executable method, int compLevel) {
if (WHITE_BOX.isMethodCompilable(method, compLevel)) {
try {
WHITE_BOX.enqueueMethodForCompilation(method, compLevel);
waitCompilation();
int tmp = WHITE_BOX.getMethodCompilationLevel(method);
if (tmp != compLevel) {
logMethod(method, "compilation level = " + tmp
+ ", but not " + compLevel);
} else if (Utils.IS_VERBOSE) {
logMethod(method, "compilation level = " + tmp + ". OK");
}
} catch (Throwable t) {
logMethod(method, "error on compile at " + compLevel
+ " level");
t.printStackTrace();
}
} else if (Utils.IS_VERBOSE) {
logMethod(method, "not compilable at " + compLevel);
}
}
private void logMethod(Executable method, String message) {
StringBuilder builder = new StringBuilder("[");
builder.append(classId);
builder.append("]\t");
builder.append(className);
builder.append("::");
builder.append(method.getName());
builder.append('(');
Class[] params = method.getParameterTypes();
for (int i = 0, n = params.length - 1; i < n; ++i) {
builder.append(params[i].getName());
builder.append(", ");
}
if (params.length != 0) {
builder.append(params[params.length - 1].getName());
}
builder.append(')');
if (message != null) {
builder.append('\t');
builder.append(message);
}
System.err.println(builder);
}
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.File;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.concurrent.Executor;
/**
* Abstract handler for path.
* <p/>
* Concrete subclasses should implement method {@link #process()}.
*
* @author igor.ignatyev@oracle.com
*/
public abstract class PathHandler {
private static final Pattern JAR_IN_DIR_PATTERN
= Pattern.compile("^(.*[/\\\\])?\\*$");
protected final Path root;
protected final Executor executor;
private ClassLoader loader;
/**
* @param root root path to process
* @param executor executor used for process task invocation
* @throws NullPointerException if {@code root} or {@code executor} is
* {@code null}
*/
protected PathHandler(Path root, Executor executor) {
Objects.requireNonNull(root);
Objects.requireNonNull(executor);
this.root = root.normalize();
this.executor = executor;
this.loader = ClassLoader.getSystemClassLoader();
}
/**
* Factory method. Construct concrete handler in depends from {@code path}.
*
* @param path the path to process
* @param executor executor used for compile task invocation
* @throws NullPointerException if {@code path} or {@code executor} is
* {@code null}
*/
public static PathHandler create(String path, Executor executor) {
Objects.requireNonNull(path);
Objects.requireNonNull(executor);
Matcher matcher = JAR_IN_DIR_PATTERN.matcher(path);
if (matcher.matches()) {
path = matcher.group(1);
path = path.isEmpty() ? "." : path;
return new ClassPathJarInDirEntry(Paths.get(path), executor);
} else {
path = path.isEmpty() ? "." : path;
Path p = Paths.get(path);
if (isJarFile(p)) {
return new ClassPathJarEntry(p, executor);
} else if (isListFile(p)) {
return new ClassesListInFile(p, executor);
} else {
return new ClassPathDirEntry(p, executor);
}
}
}
private static boolean isJarFile(Path path) {
if (Files.isRegularFile(path)) {
String name = path.toString();
return Utils.endsWithIgnoreCase(name, ".zip")
|| Utils.endsWithIgnoreCase(name, ".jar");
}
return false;
}
private static boolean isListFile(Path path) {
if (Files.isRegularFile(path)) {
String name = path.toString();
return Utils.endsWithIgnoreCase(name, ".lst");
}
return false;
}
/**
* Processes all classes in specified path.
*/
public abstract void process();
/**
* Sets class loader, that will be used to define class at
* {@link #processClass(String)}.
*
* @param loader class loader
* @throws NullPointerException if {@code loader} is {@code null}
*/
protected final void setLoader(ClassLoader loader) {
Objects.requireNonNull(loader);
this.loader = loader;
}
/**
* Processes specificed class.
* @param name fully qualified name of class to process
*/
protected final void processClass(String name) {
try {
Class aClass = Class.forName(name, true, loader);
Compiler.compileClass(aClass, executor);
} catch (ClassNotFoundException | LinkageError e) {
System.out.printf("Class %s loading failed : %s%n", name,
e.getMessage());
}
}
/**
* @return {@code true} if processing should be stopped
*/
public static boolean isFinished() {
return Compiler.isLimitReached();
}
}
/*
* 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.
*/
package sun.hotspot.tools.ctw;
import com.sun.management.HotSpotDiagnosticMXBean;
import sun.management.ManagementFactoryHelper;
import java.io.File;
import java.util.regex.Pattern;
/**
* Auxiliary methods.
*
* @author igor.ignatyev@oracle.com
*/
public class Utils {
/**
* Value of {@code -XX:CompileThreshold}
*/
public static final boolean TIERED_COMPILATION
= Boolean.parseBoolean(getVMOption("TieredCompilation", "false"));
/**
* Value of {@code -XX:BackgroundCompilation}
*/
public static final boolean BACKGROUND_COMPILATION
= Boolean.parseBoolean(getVMOption("BackgroundCompilation",
"false"));
/**
* Value of {@code -XX:TieredStopAtLevel}
*/
public static final int TIERED_STOP_AT_LEVEL;
/**
* Value of {@code -XX:CICompilerCount}
*/
public static final Integer CI_COMPILER_COUNT
= Integer.valueOf(getVMOption("CICompilerCount", "1"));
/**
* Initial compilation level.
*/
public static final int INITIAL_COMP_LEVEL;
/**
* Compiled path-separator regexp.
*/
public static final Pattern PATH_SEPARATOR = Pattern.compile(
File.pathSeparator, Pattern.LITERAL);
/**
* Value of {@code -DDeoptimizeAllClassesRate}. Frequency of
* {@code WB.deoptimizeAll()} invocation If it less that {@code 0},
* {@code WB.deoptimizeAll()} will not be invoked.
*/
public static final int DEOPTIMIZE_ALL_CLASSES_RATE
= Integer.getInteger("DeoptimizeAllClassesRate", -1);
/**
* Value of {@code -DCompileTheWorldStopAt}. Last class to consider.
*/
public static final long COMPILE_THE_WORLD_STOP_AT
= Long.getLong("CompileTheWorldStopAt", Long.MAX_VALUE);
/**
* Value of {@code -DCompileTheWorldStartAt}. First class to consider.
*/
public static final long COMPILE_THE_WORLD_START_AT
= Long.getLong("CompileTheWorldStartAt", 1);
/**
* Value of {@code -DCompileTheWorldPreloadClasses}. Preload all classes
* used by a class before start loading.
*/
public static final boolean COMPILE_THE_WORLD_PRELOAD_CLASSES;
/**
* Value of {@code -Dsun.hotspot.tools.ctw.verbose}. Verbose output,
* adds additional information about compilation.
*/
public static final boolean IS_VERBOSE
= Boolean.getBoolean("sun.hotspot.tools.ctw.verbose");
/**
* Value of {@code -Dsun.hotspot.tools.ctw.logfile}.Path to logfile, if
* it's null, cout will be used.
*/
public static final String LOG_FILE
= System.getProperty("sun.hotspot.tools.ctw.logfile");
static {
if (Utils.TIERED_COMPILATION) {
INITIAL_COMP_LEVEL = 1;
} else {
String vmName = System.getProperty("java.vm.name");
if (Utils.endsWithIgnoreCase(vmName, " Server VM")) {
INITIAL_COMP_LEVEL = 4;
} else if (Utils.endsWithIgnoreCase(vmName, " Client VM")
|| Utils.endsWithIgnoreCase(vmName, " Minimal VM")) {
INITIAL_COMP_LEVEL = 1;
} else {
throw new RuntimeException("Unknown VM: " + vmName);
}
}
TIERED_STOP_AT_LEVEL = Integer.parseInt(getVMOption("TieredStopAtLevel",
String.valueOf(INITIAL_COMP_LEVEL)));
}
static {
String tmp = System.getProperty("CompileTheWorldPreloadClasses");
if (tmp == null) {
COMPILE_THE_WORLD_PRELOAD_CLASSES = true;
} else {
COMPILE_THE_WORLD_PRELOAD_CLASSES = Boolean.parseBoolean(tmp);
}
}
public static final String CLASSFILE_EXT = ".class";
private Utils() {
}
/**
* Tests if the string ends with the suffix, ignoring case
* considerations
*
* @param string the tested string
* @param suffix the suffix
* @return {@code true} if {@code string} ends with the {@code suffix}
* @see String#endsWith(String)
*/
public static boolean endsWithIgnoreCase(String string, String suffix) {
if (string == null || suffix == null) {
return false;
}
int length = suffix.length();
int toffset = string.length() - length;
if (toffset < 0) {
return false;
}
return string.regionMatches(true, toffset, suffix, 0, length);
}
/**
* Returns value of VM option.
*
* @param name option's name
* @return value of option or {@code null}, if option doesn't exist
* @throws NullPointerException if name is null
*/
public static String getVMOption(String name) {
String result;
HotSpotDiagnosticMXBean diagnostic
= ManagementFactoryHelper.getDiagnosticMXBean();
result = diagnostic.getVMOption(name).getValue();
return result;
}
/**
* Returns value of VM option or default value.
*
* @param name option's name
* @param defaultValue default value
* @return value of option or {@code defaultValue}, if option doesn't exist
* @throws NullPointerException if name is null
* @see #getVMOption(String)
*/
public static String getVMOption(String name, String defaultValue) {
String result;
try {
result = getVMOption(name);
} catch (NoClassDefFoundError e) {
// compact1, compact2 support
result = defaultValue;
}
return result == null ? defaultValue : result;
}
/**
* Tests if the filename is valid filename for class file.
*
* @param filename tested filename
*/
public static boolean isClassFile(String filename) {
// If the filename has a period after removing '.class', it's not valid class file
return endsWithIgnoreCase(filename, CLASSFILE_EXT)
&& (filename.indexOf('.')
== (filename.length() - CLASSFILE_EXT.length()));
}
/**
* Converts the filename to classname.
*
* @param filename filename to convert
* @return corresponding classname.
* @throws AssertionError if filename isn't valid filename for class file -
* {@link #isClassFile(String)}
*/
public static String fileNameToClassName(String filename) {
assert isClassFile(filename);
return filename.substring(0, filename.length() - CLASSFILE_EXT.length())
.replace(File.separatorChar, '.');
}
}
public class Bar {
private static void staticMethod() { }
public void method() { }
protected Bar() { }
}
/*
* 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.
*/
/*
* @test ClassesDirTest
* @bug 8012447
* @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src
* @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox ClassesDirTest Foo Bar
* @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar
* @run main ClassesDirTest prepare
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld classes
* @run main ClassesDirTest check ctw.log
* @summary testing of CompileTheWorld :: classes in directory
* @author igor.ignatyev@oracle.com
*/
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class ClassesDirTest extends CtwTest {
private static final String[] SHOULD_CONTAIN
= {"# dir: classes", "Done (2 classes, 6 methods, "};
private ClassesDirTest() {
super(SHOULD_CONTAIN);
}
public static void main(String[] args) throws Exception {
new ClassesDirTest().run(args);
}
protected void prepare() throws Exception {
String path = "classes";
Files.createDirectory(Paths.get(path));
Files.move(Paths.get("Foo.class"), Paths.get(path, "Foo.class"),
StandardCopyOption.REPLACE_EXISTING);
Files.move(Paths.get("Bar.class"), Paths.get(path, "Bar.class"),
StandardCopyOption.REPLACE_EXISTING);
}
}
/*
* 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.
*/
/*
* @test ClassesListTest
* @bug 8012447
* @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src
* @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox ClassesListTest Foo Bar
* @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar
* @run main ClassesListTest prepare
* @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld classes.lst
* @run main ClassesListTest check ctw.log
* @summary testing of CompileTheWorld :: list of classes in file
* @author igor.ignatyev@oracle.com
*/
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
public class ClassesListTest extends CtwTest {
private static final String[] SHOULD_CONTAIN
= {"# list: classes.lst", "Done (4 classes, "};
private ClassesListTest() {
super(SHOULD_CONTAIN);
}
public static void main(String[] args) throws Exception {
new ClassesListTest().run(args);
}
protected void prepare() throws Exception {
String path = "classes.lst";
Files.copy(Paths.get(System.getProperty("test.src"), path),
Paths.get(path), StandardCopyOption.REPLACE_EXISTING);
}
}
/*
* 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.
*/
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
import java.io.File;
import java.io.Writer;
import java.io.FileWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.charset.Charset;
import com.oracle.java.testlibrary.JDKToolFinder;
import com.oracle.java.testlibrary.OutputAnalyzer;
public abstract class CtwTest {
protected final String[] shouldContain;
protected CtwTest(String[] shouldContain) {
this.shouldContain = shouldContain;
}
public void run(String[] args) throws Exception {
if (args.length == 0) {
throw new Error("args is empty");
}
switch (args[0]) {
case "prepare":
prepare();
break;
case "check":
check(args);
break;
default:
throw new Error("unregonized action -- " + args[0]);
}
}
protected void prepare() throws Exception { }
protected void check(String[] args) throws Exception {
if (args.length < 2) {
throw new Error("logfile isn't specified");
}
String logfile = args[1];
try (BufferedReader r = Files.newBufferedReader(Paths.get(logfile),
Charset.defaultCharset())) {
OutputAnalyzer output = readOutput(r);
for (String test : shouldContain) {
output.shouldContain(test);
}
}
}
private static OutputAnalyzer readOutput(BufferedReader reader)
throws IOException {
StringBuilder builder = new StringBuilder();
String eol = String.format("%n");
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(eol);
}
return new OutputAnalyzer(builder.toString(), "");
}
protected void dump(OutputAnalyzer output, String name) {
try (Writer w = new FileWriter(name + ".out")) {
String s = output.getStdout();
w.write(s, s.length(), 0);
} catch (IOException io) {
io.printStackTrace();
}
try (Writer w = new FileWriter(name + ".err")) {
String s = output.getStderr();
w.write(s, s.length(), 0);
} catch (IOException io) {
io.printStackTrace();
}
}
protected ProcessBuilder createJarProcessBuilder(String... command)
throws Exception {
String javapath = JDKToolFinder.getJDKTool("jar");
ArrayList<String> args = new ArrayList<>();
args.add(javapath);
Collections.addAll(args, command);
return new ProcessBuilder(args.toArray(new String[args.size()]));
}
}
public class Foo {
private static void staticMethod() { }
public void method() { }
protected Foo() { }
}
/*
* 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.
*/
/*
* @test JarDirTest
* @bug 8012447
* @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src
* @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox JarDirTest Foo Bar
* @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar
* @run main JarDirTest prepare
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld jars/*
* @run main JarDirTest check ctw.log
* @summary testing of CompileTheWorld :: jars in directory
* @author igor.ignatyev@oracle.com
*/
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.oracle.java.testlibrary.OutputAnalyzer;
public class JarDirTest extends CtwTest {
private static final String[] SHOULD_CONTAIN
= {"# jar_in_dir: jars",
"# jar: jars" + File.separator +"foo.jar",
"# jar: jars" + File.separator +"bar.jar",
"Done (4 classes, 12 methods, "};
private JarDirTest() {
super(SHOULD_CONTAIN);
}
public static void main(String[] args) throws Exception {
new JarDirTest().run(args);
}
protected void prepare() throws Exception {
String path = "jars";
Files.createDirectory(Paths.get(path));
ProcessBuilder pb = createJarProcessBuilder("cf", "jars/foo.jar",
"Foo.class", "Bar.class");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
dump(output, "ctw-foo.jar");
output.shouldHaveExitValue(0);
pb = createJarProcessBuilder("cf", "jars/bar.jar", "Foo.class",
"Bar.class");
output = new OutputAnalyzer(pb.start());
dump(output, "ctw-bar.jar");
output.shouldHaveExitValue(0);
}
}
/*
* 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.
*/
/*
* @test JarsTest
* @bug 8012447
* @library /testlibrary /testlibrary/whitebox /testlibrary/ctw/src
* @build sun.hotspot.tools.ctw.CompileTheWorld sun.hotspot.WhiteBox JarsTest Foo Bar
* @run main ClassFileInstaller sun.hotspot.WhiteBox Foo Bar
* @run main JarsTest prepare
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Dsun.hotspot.tools.ctw.logfile=ctw.log sun.hotspot.tools.ctw.CompileTheWorld foo.jar bar.jar
* @run main JarsTest check ctw.log
* @summary testing of CompileTheWorld :: jars
* @author igor.ignatyev@oracle.com
*/
import com.oracle.java.testlibrary.OutputAnalyzer;
public class JarsTest extends CtwTest {
private static final String[] SHOULD_CONTAIN
= {"# jar: foo.jar", "# jar: bar.jar",
"Done (4 classes, 12 methods, "};
private JarsTest() {
super(SHOULD_CONTAIN);
}
public static void main(String[] args) throws Exception {
new JarsTest().run(args);
}
protected void prepare() throws Exception {
ProcessBuilder pb = createJarProcessBuilder("cf", "foo.jar",
"Foo.class", "Bar.class");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
dump(output, "ctw-foo.jar");
output.shouldHaveExitValue(0);
pb = createJarProcessBuilder("cf", "bar.jar", "Foo.class", "Bar.class");
output = new OutputAnalyzer(pb.start());
dump(output, "ctw-bar.jar");
output.shouldHaveExitValue(0);
}
}
java.lang.String
java.lang.Object
Foo
Bar
#
# 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.
#
#
ifneq "x$(ALT_BOOTDIR)" "x"
BOOTDIR := $(ALT_BOOTDIR)
endif
ifeq "x$(BOOTDIR)" "x"
JDK_HOME := $(shell dirname $(shell which java))/..
else
JDK_HOME := $(BOOTDIR)
endif
SRC_DIR = ./
BUILD_DIR = build
OUTPUT_DIR = $(BUILD_DIR)/classes
JAVAC = $(JDK_HOME)/bin/javac
JAR = $(JDK_HOME)/bin/jar
SRC_FILES = $(shell find $(SRC_DIR) -name '*.java')
.PHONY: filelist clean cleantmp
all: wb.jar cleantmp
wb.jar: filelist
@mkdir -p $(OUTPUT_DIR)
$(JAVAC) -sourcepath $(SRC_DIR) -d $(OUTPUT_DIR) -cp $(OUTPUT_DIR) @filelist
$(JAR) cf wb.jar -C $(OUTPUT_DIR) .
@rm -rf $(OUTPUT_DIR)
filelist: $(SRC_FILES)
@rm -f $@
@echo $(SRC_FILES) > $@
clean: cleantmp
@rm -rf wb.jar
cleantmp:
@rm -rf filelist
@rm -rf $(BUILD_DIR)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册