mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
Compare commits
45 Commits
jdk-23+1
...
jdk-18.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4a39323bf | ||
|
|
eb2f497cf0 | ||
|
|
41fe8d2200 | ||
|
|
71640f2870 | ||
|
|
84afe806bd | ||
|
|
7796859f04 | ||
|
|
f1c769462d | ||
|
|
fbc28a7047 | ||
|
|
1f039b3e1d | ||
|
|
68a08070ca | ||
|
|
93453364ed | ||
|
|
d21b5e77fa | ||
|
|
34f2864680 | ||
|
|
313932af0a | ||
|
|
2b74a8d2a1 | ||
|
|
e32fb7fc20 | ||
|
|
c3653f2400 | ||
|
|
355f7fe2c5 | ||
|
|
74e1fdbb12 | ||
|
|
c305c3e18e | ||
|
|
e161e135c0 | ||
|
|
f32637c0ba | ||
|
|
5b8f9ff39a | ||
|
|
86956ee964 | ||
|
|
cd5a9a9522 | ||
|
|
bb5bc6a87e | ||
|
|
76a2b3bfaf | ||
|
|
a9622b1c93 | ||
|
|
fcd258a07f | ||
|
|
8b416ea00c | ||
|
|
56b0dfffb7 | ||
|
|
715b432ae5 | ||
|
|
be731606e2 | ||
|
|
338d3d2d8a | ||
|
|
29ce3ae34b | ||
|
|
bab7d80317 | ||
|
|
d0e26d3e7c | ||
|
|
f357fdefad | ||
|
|
6caa851218 | ||
|
|
cbcb39119d | ||
|
|
a2d1450011 | ||
|
|
b260c5c939 | ||
|
|
419f9ccd83 | ||
|
|
d2ee0647a5 | ||
|
|
6d45bba88a |
@@ -1,6 +1,7 @@
|
||||
[general]
|
||||
project=jdk
|
||||
project=jdk-updates
|
||||
jbs=JDK
|
||||
version=18.0.1
|
||||
|
||||
[checks]
|
||||
error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists
|
||||
|
||||
@@ -28,15 +28,15 @@
|
||||
|
||||
DEFAULT_VERSION_FEATURE=18
|
||||
DEFAULT_VERSION_INTERIM=0
|
||||
DEFAULT_VERSION_UPDATE=0
|
||||
DEFAULT_VERSION_UPDATE=1
|
||||
DEFAULT_VERSION_PATCH=0
|
||||
DEFAULT_VERSION_EXTRA1=0
|
||||
DEFAULT_VERSION_EXTRA2=0
|
||||
DEFAULT_VERSION_EXTRA3=0
|
||||
DEFAULT_VERSION_DATE=2022-03-22
|
||||
DEFAULT_VERSION_DATE=2022-04-19
|
||||
DEFAULT_VERSION_CLASSFILE_MAJOR=62 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
||||
DEFAULT_VERSION_CLASSFILE_MINOR=0
|
||||
DEFAULT_VERSION_DOCS_API_SINCE=11
|
||||
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="17 18"
|
||||
DEFAULT_JDK_SOURCE_TARGET_VERSION=18
|
||||
DEFAULT_PROMOTED_VERSION_PRE=ea
|
||||
DEFAULT_PROMOTED_VERSION_PRE=
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
# Create a bundle in the build directory, containing what's needed to
|
||||
# build and run JMH microbenchmarks from the OpenJDK build.
|
||||
|
||||
JMH_VERSION=1.33
|
||||
JMH_VERSION=1.34
|
||||
COMMONS_MATH3_VERSION=3.2
|
||||
JOPT_SIMPLE_VERSION=4.6
|
||||
|
||||
|
||||
@@ -1820,8 +1820,8 @@ void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2,
|
||||
__ teq(xhi, yhi);
|
||||
__ teq(xlo, ylo, eq);
|
||||
} else {
|
||||
__ subs(xlo, xlo, ylo);
|
||||
__ sbcs(xhi, xhi, yhi);
|
||||
__ subs(Rtemp, xlo, ylo);
|
||||
__ sbcs(Rtemp, xhi, yhi);
|
||||
}
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
|
||||
@@ -1607,9 +1607,14 @@ const bool Matcher::match_rule_supported(int opcode) {
|
||||
}
|
||||
break;
|
||||
case Op_SqrtD:
|
||||
#ifdef _LP64
|
||||
if (UseSSE < 2) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// x86_32.ad has a special match rule for SqrtD.
|
||||
// Together with common x86 rules, this handles all UseSSE cases.
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
return true; // Match rules are supported by default.
|
||||
|
||||
@@ -1970,6 +1970,8 @@ void CompileBroker::compiler_thread_loop() {
|
||||
method->clear_queued_for_compilation();
|
||||
task->set_failure_reason("compilation is disabled");
|
||||
}
|
||||
} else {
|
||||
task->set_failure_reason("breakpoints are present");
|
||||
}
|
||||
|
||||
if (UseDynamicNumberOfCompilerThreads) {
|
||||
|
||||
@@ -1029,7 +1029,6 @@ int ExceptionMessageBuilder::do_instruction(int bci) {
|
||||
break;
|
||||
|
||||
case Bytecodes::_arraylength:
|
||||
// The return type of arraylength is wrong in the bytecodes table (T_VOID).
|
||||
stack->pop(1);
|
||||
stack->push(bci, T_INT);
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2021, 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
|
||||
@@ -471,7 +471,7 @@ void Bytecodes::initialize() {
|
||||
def(_new , "new" , "bkk" , NULL , T_OBJECT , 1, true );
|
||||
def(_newarray , "newarray" , "bc" , NULL , T_OBJECT , 0, true );
|
||||
def(_anewarray , "anewarray" , "bkk" , NULL , T_OBJECT , 0, true );
|
||||
def(_arraylength , "arraylength" , "b" , NULL , T_VOID , 0, true );
|
||||
def(_arraylength , "arraylength" , "b" , NULL , T_INT , 0, true );
|
||||
def(_athrow , "athrow" , "b" , NULL , T_VOID , -1, true );
|
||||
def(_checkcast , "checkcast" , "bkk" , NULL , T_OBJECT , 0, true );
|
||||
def(_instanceof , "instanceof" , "bkk" , NULL , T_INT , 0, true );
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@@ -2376,7 +2376,7 @@ C2V_VMENTRY_PREFIX(void, detachCurrentThread, (JNIEnv* env, jobject c2vm))
|
||||
}
|
||||
C2V_END
|
||||
|
||||
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
|
||||
C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jboolean callPostTranslation))
|
||||
requireJVMCINativeLibrary(JVMCI_CHECK_0);
|
||||
if (obj_handle == NULL) {
|
||||
return 0L;
|
||||
@@ -2427,7 +2427,9 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
|
||||
const char* cstring = name_string.is_null() ? NULL : thisEnv->as_utf8_string(name_string);
|
||||
// Create a new HotSpotNmethod instance in the peer runtime
|
||||
result = peerEnv->new_HotSpotNmethod(mh, cstring, isDefault, compileIdSnapshot, JVMCI_CHECK_0);
|
||||
if (nm == NULL) {
|
||||
if (result.is_null()) {
|
||||
// exception occurred (e.g. OOME) creating a new HotSpotNmethod
|
||||
} else if (nm == NULL) {
|
||||
// nmethod must have been unloaded
|
||||
} else {
|
||||
// Link the new HotSpotNmethod to the nmethod
|
||||
@@ -2450,6 +2452,13 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle))
|
||||
JVMCI_THROW_MSG_0(IllegalArgumentException,
|
||||
err_msg("Cannot translate object of type: %s", thisEnv->klass_name(obj)));
|
||||
}
|
||||
if (callPostTranslation) {
|
||||
peerEnv->call_HotSpotJVMCIRuntime_postTranslation(result, JVMCI_CHECK_0);
|
||||
}
|
||||
// Propagate any exception that occurred while creating the translated object
|
||||
if (peerEnv->transfer_pending_exception(thread, thisEnv)) {
|
||||
return 0L;
|
||||
}
|
||||
return (jlong) peerEnv->make_global(result).as_jobject();
|
||||
}
|
||||
|
||||
@@ -2790,7 +2799,7 @@ JNINativeMethod CompilerToVM::methods[] = {
|
||||
{CC "getCurrentJavaThread", CC "()J", FN_PTR(getCurrentJavaThread)},
|
||||
{CC "attachCurrentThread", CC "([BZ)Z", FN_PTR(attachCurrentThread)},
|
||||
{CC "detachCurrentThread", CC "()V", FN_PTR(detachCurrentThread)},
|
||||
{CC "translate", CC "(" OBJECT ")J", FN_PTR(translate)},
|
||||
{CC "translate", CC "(" OBJECT "Z)J", FN_PTR(translate)},
|
||||
{CC "unhand", CC "(J)" OBJECT, FN_PTR(unhand)},
|
||||
{CC "updateHotSpotNmethod", CC "(" HS_NMETHOD ")V", FN_PTR(updateHotSpotNmethod)},
|
||||
{CC "getCode", CC "(" HS_INSTALLED_CODE ")[B", FN_PTR(getCode)},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@@ -278,35 +278,141 @@ void JVMCIEnv::describe_pending_exception(bool clear) {
|
||||
}
|
||||
}
|
||||
|
||||
void JVMCIEnv::translate_hotspot_exception_to_jni_exception(JavaThread* THREAD, const Handle& throwable) {
|
||||
assert(!is_hotspot(), "must_be");
|
||||
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
|
||||
// may not have been called.
|
||||
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_oop(throwable);
|
||||
JavaValue result(T_OBJECT);
|
||||
JavaCalls::call_static(&result,
|
||||
runtimeKlass,
|
||||
vmSymbols::encodeThrowable_name(),
|
||||
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
JVMCIRuntime::fatal_exception(this, "HotSpotJVMCIRuntime.encodeThrowable should not throw an exception");
|
||||
// Shared code for translating an exception from HotSpot to libjvmci or vice versa.
|
||||
class ExceptionTranslation: public StackObj {
|
||||
protected:
|
||||
JVMCIEnv* _from_env; // Source of translation. Can be nullptr.
|
||||
JVMCIEnv* _to_env; // Destination of translation. Never nullptr.
|
||||
|
||||
ExceptionTranslation(JVMCIEnv* from_env, JVMCIEnv* to_env) : _from_env(from_env), _to_env(to_env) {}
|
||||
|
||||
// Encodes the exception in `_from_env` into `buffer`.
|
||||
// Where N is the number of bytes needed for the encoding, returns N if N <= `buffer_size`
|
||||
// and the encoding was written to `buffer` otherwise returns -N.
|
||||
virtual int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) = 0;
|
||||
|
||||
// Decodes the exception in `buffer` in `_to_env` and throws it.
|
||||
virtual void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) = 0;
|
||||
|
||||
public:
|
||||
void doit(JavaThread* THREAD) {
|
||||
// Resolve HotSpotJVMCIRuntime class explicitly as HotSpotJVMCI::compute_offsets
|
||||
// may not have been called.
|
||||
Klass* runtimeKlass = SystemDictionary::resolve_or_fail(vmSymbols::jdk_vm_ci_hotspot_HotSpotJVMCIRuntime(), true, CHECK);
|
||||
|
||||
int buffer_size = 2048;
|
||||
while (true) {
|
||||
ResourceMark rm;
|
||||
jlong buffer = (jlong) NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, jbyte, buffer_size);
|
||||
int res = encode(THREAD, runtimeKlass, buffer, buffer_size);
|
||||
if ((_from_env != nullptr && _from_env->has_pending_exception()) || HAS_PENDING_EXCEPTION) {
|
||||
JVMCIRuntime::fatal_exception(_from_env, "HotSpotJVMCIRuntime.encodeThrowable should not throw an exception");
|
||||
}
|
||||
if (res < 0) {
|
||||
int required_buffer_size = -res;
|
||||
if (required_buffer_size > buffer_size) {
|
||||
buffer_size = required_buffer_size;
|
||||
}
|
||||
} else {
|
||||
decode(THREAD, runtimeKlass, buffer);
|
||||
if (!_to_env->has_pending_exception()) {
|
||||
JVMCIRuntime::fatal_exception(_to_env, "HotSpotJVMCIRuntime.decodeAndThrowThrowable should throw an exception");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Translates an exception on the HotSpot heap to an exception on the shared library heap.
|
||||
class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
|
||||
private:
|
||||
const Handle& _throwable;
|
||||
|
||||
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_oop(_throwable);
|
||||
jargs.push_long(buffer);
|
||||
jargs.push_int(buffer_size);
|
||||
JavaValue result(T_INT);
|
||||
JavaCalls::call_static(&result,
|
||||
runtimeKlass,
|
||||
vmSymbols::encodeThrowable_name(),
|
||||
vmSymbols::encodeThrowable_signature(), &jargs, THREAD);
|
||||
return result.get_jint();
|
||||
}
|
||||
|
||||
oop encoded_throwable_string = result.get_oop();
|
||||
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
|
||||
JNIAccessMark jni(_to_env, THREAD);
|
||||
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::decodeAndThrowThrowable_method(),
|
||||
buffer);
|
||||
}
|
||||
public:
|
||||
HotSpotToSharedLibraryExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, const Handle& throwable) :
|
||||
ExceptionTranslation(hotspot_env, jni_env), _throwable(throwable) {}
|
||||
};
|
||||
|
||||
ResourceMark rm;
|
||||
const char* encoded_throwable_chars = java_lang_String::as_utf8_string(encoded_throwable_string);
|
||||
// Translates an exception on the shared library heap to an exception on the HotSpot heap.
|
||||
class SharedLibraryToHotSpotExceptionTranslation : public ExceptionTranslation {
|
||||
private:
|
||||
jthrowable _throwable;
|
||||
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
jobject jni_encoded_throwable_string = jni()->NewStringUTF(encoded_throwable_chars);
|
||||
jthrowable jni_throwable = (jthrowable) jni()->CallStaticObjectMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::decodeThrowable_method(),
|
||||
jni_encoded_throwable_string);
|
||||
jni()->Throw(jni_throwable);
|
||||
int encode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer, int buffer_size) {
|
||||
JNIAccessMark jni(_from_env, THREAD);
|
||||
return jni()->CallStaticIntMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::encodeThrowable_method(),
|
||||
_throwable, buffer, buffer_size);
|
||||
}
|
||||
|
||||
void decode(JavaThread* THREAD, Klass* runtimeKlass, jlong buffer) {
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_long(buffer);
|
||||
JavaValue result(T_VOID);
|
||||
JavaCalls::call_static(&result,
|
||||
runtimeKlass,
|
||||
vmSymbols::decodeAndThrowThrowable_name(),
|
||||
vmSymbols::long_void_signature(), &jargs, THREAD);
|
||||
}
|
||||
public:
|
||||
SharedLibraryToHotSpotExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, jthrowable throwable) :
|
||||
ExceptionTranslation(jni_env, hotspot_env), _throwable(throwable) {}
|
||||
};
|
||||
|
||||
void JVMCIEnv::translate_to_jni_exception(JavaThread* THREAD, const Handle& throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
|
||||
HotSpotToSharedLibraryExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
|
||||
}
|
||||
|
||||
void JVMCIEnv::translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env) {
|
||||
SharedLibraryToHotSpotExceptionTranslation(hotspot_env, jni_env, throwable).doit(THREAD);
|
||||
}
|
||||
|
||||
jboolean JVMCIEnv::transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env) {
|
||||
if (is_hotspot()) {
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
translate_to_jni_exception(THREAD, throwable, this, peer_env);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
jthrowable ex = nullptr;
|
||||
{
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
ex = jni()->ExceptionOccurred();
|
||||
if (ex != nullptr) {
|
||||
jni()->ExceptionClear();
|
||||
}
|
||||
}
|
||||
if (ex != nullptr) {
|
||||
translate_from_jni_exception(THREAD, ex, peer_env, this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
JVMCIEnv::~JVMCIEnv() {
|
||||
if (_throw_to_caller) {
|
||||
if (is_hotspot()) {
|
||||
@@ -318,7 +424,7 @@ JVMCIEnv::~JVMCIEnv() {
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
translate_hotspot_exception_to_jni_exception(THREAD, throwable);
|
||||
translate_to_jni_exception(THREAD, throwable, nullptr, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -801,6 +907,23 @@ JVMCIObject JVMCIEnv::call_HotSpotJVMCIRuntime_callToString(JVMCIObject object,
|
||||
}
|
||||
}
|
||||
|
||||
void JVMCIEnv::call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCIEnv* JVMCIENV) {
|
||||
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
|
||||
if (is_hotspot()) {
|
||||
JavaCallArguments jargs;
|
||||
jargs.push_oop(Handle(THREAD, HotSpotJVMCI::resolve(object)));
|
||||
JavaValue result(T_VOID);
|
||||
JavaCalls::call_static(&result,
|
||||
HotSpotJVMCI::HotSpotJVMCIRuntime::klass(),
|
||||
vmSymbols::postTranslation_name(),
|
||||
vmSymbols::object_void_signature(), &jargs, CHECK);
|
||||
} else {
|
||||
JNIAccessMark jni(this, THREAD);
|
||||
jni()->CallStaticVoidMethod(JNIJVMCI::HotSpotJVMCIRuntime::clazz(),
|
||||
JNIJVMCI::HotSpotJVMCIRuntime::postTranslation_method(),
|
||||
object.as_jobject());
|
||||
}
|
||||
}
|
||||
|
||||
JVMCIObject JVMCIEnv::call_JavaConstant_forPrimitive(JVMCIObject kind, jlong value, JVMCI_TRAPS) {
|
||||
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@@ -171,11 +171,15 @@ class JVMCIEnv : public ResourceObj {
|
||||
const char* _file; // The file and ...
|
||||
int _line; // ... line where this JNIEnv was created
|
||||
|
||||
// Translates an exception on the HotSpot heap to an exception on
|
||||
// the shared library heap. The translation includes the stack and
|
||||
// causes of `throwable`. The translated exception is pending in the
|
||||
// shared library thread upon returning.
|
||||
void translate_hotspot_exception_to_jni_exception(JavaThread* THREAD, const Handle& throwable);
|
||||
// Translates an exception on the HotSpot heap (i.e., hotspot_env) to an exception on
|
||||
// the shared library heap (i.e., jni_env). The translation includes the stack and cause(s) of `throwable`.
|
||||
// The translated exception is pending in jni_env upon returning.
|
||||
static void translate_to_jni_exception(JavaThread* THREAD, const Handle& throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
|
||||
|
||||
// Translates an exception on the shared library heap (i.e., jni_env) to an exception on
|
||||
// the HotSpot heap (i.e., hotspot_env). The translation includes the stack and cause(s) of `throwable`.
|
||||
// The translated exception is pending in hotspot_env upon returning.
|
||||
static void translate_from_jni_exception(JavaThread* THREAD, jthrowable throwable, JVMCIEnv* hotspot_env, JVMCIEnv* jni_env);
|
||||
|
||||
public:
|
||||
// Opens a JVMCIEnv scope for a Java to VM call (e.g., via CompilerToVM).
|
||||
@@ -225,6 +229,11 @@ public:
|
||||
jboolean has_pending_exception();
|
||||
void clear_pending_exception();
|
||||
|
||||
// If this env has a pending exception, it is translated to be a pending
|
||||
// exception in `peer_env` and is cleared from this env. Returns true
|
||||
// if a pending exception was transferred, false otherwise.
|
||||
jboolean transfer_pending_exception(JavaThread* THREAD, JVMCIEnv* peer_env);
|
||||
|
||||
// Prints an exception and stack trace of a pending exception.
|
||||
void describe_pending_exception(bool clear);
|
||||
|
||||
@@ -311,6 +320,8 @@ public:
|
||||
|
||||
jboolean call_HotSpotJVMCIRuntime_isGCSupported(JVMCIObject runtime, jint gcIdentifier);
|
||||
|
||||
void call_HotSpotJVMCIRuntime_postTranslation(JVMCIObject object, JVMCI_TRAPS);
|
||||
|
||||
BasicType kindToBasicType(JVMCIObject kind, JVMCI_TRAPS);
|
||||
|
||||
#define DO_THROW(name) \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
|
||||
@@ -349,13 +349,14 @@
|
||||
objectarray_field(HotSpotJVMCIRuntime, excludeFromJVMCICompilation, "[Ljava/lang/Module;") \
|
||||
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, compileMethod, compileMethod_signature, (JVMCIObject runtime, JVMCIObject method, int entry_bci, jlong env, int id)) \
|
||||
jvmci_method(CallNonvirtualObjectMethod, GetMethodID, call_special, JVMCIObject, HotSpotJVMCIRuntime, isGCSupported, int_bool_signature, (JVMCIObject runtime, int gcIdentifier)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, decodeThrowable, decodeThrowable_signature, (JVMCIObject encodedThrowable)) \
|
||||
jvmci_method(CallStaticBooleanMethod, GetStaticMethodID, call_static, bool, HotSpotJVMCIRuntime, encodeThrowable, encodeThrowable_signature, (JVMCIObject throwable, jlong buffer, int buffer_size)) \
|
||||
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, decodeAndThrowThrowable, long_void_signature, (jlong buffer)) \
|
||||
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, bootstrapFinished, void_method_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
|
||||
jvmci_method(CallNonvirtualVoidMethod, GetMethodID, call_special, void, HotSpotJVMCIRuntime, shutdown, void_method_signature, (JVMCIObject runtime)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, runtime, runtime_signature, (JVMCI_TRAPS)) \
|
||||
jvmci_method(CallObjectMethod, GetMethodID, call_virtual, JVMCIObject, HotSpotJVMCIRuntime, getCompiler, getCompiler_signature, (JVMCIObject runtime, JVMCI_TRAPS)) \
|
||||
jvmci_method(CallStaticObjectMethod, GetStaticMethodID, call_static, JVMCIObject, HotSpotJVMCIRuntime, callToString, callToString_signature, (JVMCIObject object, JVMCI_TRAPS)) \
|
||||
jvmci_method(CallStaticVoidMethod, GetStaticMethodID, call_static, void, HotSpotJVMCIRuntime, postTranslation, object_void_signature, (JVMCIObject object, JVMCI_TRAPS)) \
|
||||
end_class \
|
||||
start_class(JVMCIError, jdk_vm_ci_common_JVMCIError) \
|
||||
jvmci_constructor(JVMCIError, "(Ljava/lang/String;)V") \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2022, 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
|
||||
@@ -105,9 +105,8 @@
|
||||
template(compileMethod_signature, "(Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;IJI)Ljdk/vm/ci/hotspot/HotSpotCompilationRequestResult;") \
|
||||
template(isGCSupported_name, "isGCSupported") \
|
||||
template(encodeThrowable_name, "encodeThrowable") \
|
||||
template(encodeThrowable_signature, "(Ljava/lang/Throwable;)Ljava/lang/String;") \
|
||||
template(decodeThrowable_name, "decodeThrowable") \
|
||||
template(decodeThrowable_signature, "(Ljava/lang/String;)Ljava/lang/Throwable;") \
|
||||
template(encodeThrowable_signature, "(Ljava/lang/Throwable;JI)I") \
|
||||
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
|
||||
template(fromMetaspace_name, "fromMetaspace") \
|
||||
template(method_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotResolvedJavaMethod;") \
|
||||
template(constantPool_fromMetaspace_signature, "(J)Ljdk/vm/ci/hotspot/HotSpotConstantPool;") \
|
||||
@@ -123,6 +122,7 @@
|
||||
template(getCompiler_signature, "()Ljdk/vm/ci/runtime/JVMCICompiler;") \
|
||||
template(callToString_name, "callToString") \
|
||||
template(callToString_signature, "(Ljava/lang/Object;)Ljava/lang/String;") \
|
||||
template(postTranslation_name, "postTranslation") \
|
||||
template(getName_name, "getName") \
|
||||
template(bootstrapFinished_name, "bootstrapFinished") \
|
||||
template(forPrimitive_name, "forPrimitive") \
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@@ -1580,7 +1580,7 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) {
|
||||
if (is_store) {
|
||||
access_store_at(value, adr, TypeAryPtr::BYTES, ch, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED);
|
||||
} else {
|
||||
ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD);
|
||||
ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD | C2_UNKNOWN_CONTROL_LOAD);
|
||||
set_result(ch);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1024,13 +1024,15 @@ private:
|
||||
GrowableArray<float> _freqs; // cache frequencies
|
||||
PhaseIdealLoop* _phase;
|
||||
|
||||
void set_rounding(int mode) {
|
||||
// fesetround is broken on windows
|
||||
NOT_WINDOWS(fesetround(mode);)
|
||||
}
|
||||
|
||||
void check_frequency(float f) {
|
||||
NOT_WINDOWS(assert(f <= 1 && f >= 0, "Incorrect frequency");)
|
||||
float check_and_truncate_frequency(float f) {
|
||||
assert(f >= 0, "Incorrect frequency");
|
||||
// We do not perform an exact (f <= 1) check
|
||||
// this would be error prone with rounding of floats.
|
||||
// Performing a check like (f <= 1+eps) would be of benefit,
|
||||
// however, it is not evident how to determine such an eps,
|
||||
// given that an arbitrary number of add/mul operations
|
||||
// are performed on these frequencies.
|
||||
return (f > 1) ? 1 : f;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1040,7 +1042,6 @@ public:
|
||||
|
||||
float to(Node* n) {
|
||||
// post order walk on the CFG graph from n to _dom
|
||||
set_rounding(FE_TOWARDZERO); // make sure rounding doesn't push frequency above 1
|
||||
IdealLoopTree* loop = _phase->get_loop(_dom);
|
||||
Node* c = n;
|
||||
for (;;) {
|
||||
@@ -1067,14 +1068,12 @@ public:
|
||||
inner_head = inner_loop->_head->as_Loop();
|
||||
inner_head->verify_strip_mined(1);
|
||||
}
|
||||
set_rounding(FE_UPWARD); // make sure rounding doesn't push frequency above 1
|
||||
float loop_exit_cnt = 0.0f;
|
||||
for (uint i = 0; i < inner_loop->_body.size(); i++) {
|
||||
Node *n = inner_loop->_body[i];
|
||||
float c = inner_loop->compute_profile_trip_cnt_helper(n);
|
||||
loop_exit_cnt += c;
|
||||
}
|
||||
set_rounding(FE_TOWARDZERO);
|
||||
float cnt = -1;
|
||||
if (n->in(0)->is_If()) {
|
||||
IfNode* iff = n->in(0)->as_If();
|
||||
@@ -1094,9 +1093,9 @@ public:
|
||||
cnt = p * jmp->_fcnt;
|
||||
}
|
||||
float this_exit_f = cnt > 0 ? cnt / loop_exit_cnt : 0;
|
||||
check_frequency(this_exit_f);
|
||||
this_exit_f = check_and_truncate_frequency(this_exit_f);
|
||||
f = f * this_exit_f;
|
||||
check_frequency(f);
|
||||
f = check_and_truncate_frequency(f);
|
||||
} else {
|
||||
float p = -1;
|
||||
if (n->in(0)->is_If()) {
|
||||
@@ -1109,7 +1108,7 @@ public:
|
||||
p = n->in(0)->as_Jump()->_probs[n->as_JumpProj()->_con];
|
||||
}
|
||||
f = f * p;
|
||||
check_frequency(f);
|
||||
f = check_and_truncate_frequency(f);
|
||||
}
|
||||
_freqs.at_put_grow(n->_idx, (float)f, -1);
|
||||
_stack.pop();
|
||||
@@ -1117,7 +1116,7 @@ public:
|
||||
float prev_f = _freqs_stack.pop();
|
||||
float new_f = f;
|
||||
f = new_f + prev_f;
|
||||
check_frequency(f);
|
||||
f = check_and_truncate_frequency(f);
|
||||
uint i = _stack.index();
|
||||
if (i < n->req()) {
|
||||
c = n->in(i);
|
||||
@@ -1130,9 +1129,7 @@ public:
|
||||
}
|
||||
}
|
||||
if (_stack.size() == 0) {
|
||||
set_rounding(FE_TONEAREST);
|
||||
check_frequency(f);
|
||||
return f;
|
||||
return check_and_truncate_frequency(f);
|
||||
}
|
||||
} else if (c->is_Loop()) {
|
||||
ShouldNotReachHere();
|
||||
|
||||
@@ -75,21 +75,25 @@
|
||||
// Return a node which is more "ideal" than the current node.
|
||||
// Move constants to the right.
|
||||
Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if( in(0) && remove_dead_region(phase, can_reshape) ) return this;
|
||||
if (in(0) != NULL && remove_dead_region(phase, can_reshape)) {
|
||||
return this;
|
||||
}
|
||||
// Don't bother trying to transform a dead node
|
||||
if( in(0) && in(0)->is_top() ) return NULL;
|
||||
if (in(0) != NULL && in(0)->is_top()) {
|
||||
return NULL;
|
||||
}
|
||||
assert(in(Condition) != this &&
|
||||
in(IfFalse) != this &&
|
||||
in(IfTrue) != this, "dead loop in CMoveNode::Ideal" );
|
||||
if( phase->type(in(Condition)) == Type::TOP )
|
||||
return NULL; // return NULL when Condition is dead
|
||||
|
||||
if( in(IfFalse)->is_Con() && !in(IfTrue)->is_Con() ) {
|
||||
if( in(Condition)->is_Bool() ) {
|
||||
BoolNode* b = in(Condition)->as_Bool();
|
||||
BoolNode* b2 = b->negate(phase);
|
||||
return make(in(Control), phase->transform(b2), in(IfTrue), in(IfFalse), _type);
|
||||
}
|
||||
in(IfFalse) != this &&
|
||||
in(IfTrue) != this, "dead loop in CMoveNode::Ideal");
|
||||
if (phase->type(in(Condition)) == Type::TOP ||
|
||||
phase->type(in(IfFalse)) == Type::TOP ||
|
||||
phase->type(in(IfTrue)) == Type::TOP) {
|
||||
return NULL;
|
||||
}
|
||||
// Canonicalize the node by moving constants to the right input.
|
||||
if (in(Condition)->is_Bool() && phase->type(in(IfFalse))->singleton() && !phase->type(in(IfTrue))->singleton()) {
|
||||
BoolNode* b = in(Condition)->as_Bool()->negate(phase);
|
||||
return make(in(Control), phase->transform(b), in(IfTrue), in(IfFalse), _type);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -191,14 +195,10 @@ Node *CMoveINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
// If zero is on the left (false-case, no-move-case) it must mean another
|
||||
// constant is on the right (otherwise the shared CMove::Ideal code would
|
||||
// have moved the constant to the right). This situation is bad for Intel
|
||||
// and a don't-care for Sparc. It's bad for Intel because the zero has to
|
||||
// be manifested in a register with a XOR which kills flags, which are live
|
||||
// on input to the CMoveI, leading to a situation which causes excessive
|
||||
// spilling on Intel. For Sparc, if the zero in on the left the Sparc will
|
||||
// zero a register via G0 and conditionally-move the other constant. If the
|
||||
// zero is on the right, the Sparc will load the first constant with a
|
||||
// 13-bit set-lo and conditionally move G0. See bug 4677505.
|
||||
// have moved the constant to the right). This situation is bad for x86 because
|
||||
// the zero has to be manifested in a register with a XOR which kills flags,
|
||||
// which are live on input to the CMoveI, leading to a situation which causes
|
||||
// excessive spilling. See bug 4677505.
|
||||
if( phase->type(in(IfFalse)) == TypeInt::ZERO && !(phase->type(in(IfTrue)) == TypeInt::ZERO) ) {
|
||||
if( in(Condition)->is_Bool() ) {
|
||||
BoolNode* b = in(Condition)->as_Bool();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@@ -68,6 +68,25 @@ public final class KeychainStore extends KeyStoreSpi {
|
||||
|
||||
Certificate cert;
|
||||
long certRef; // SecCertificateRef for this key
|
||||
|
||||
// Each KeyStore.TrustedCertificateEntry have 2 attributes:
|
||||
// 1. "trustSettings" -> trustSettings.toString()
|
||||
// 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue
|
||||
// The 1st one is mainly for debugging use. The 2nd one is similar
|
||||
// to the attribute with the same key in a PKCS12KeyStore.
|
||||
|
||||
// The SecTrustSettingsCopyTrustSettings() output for this certificate
|
||||
// inside the KeyChain in its original array of CFDictionaryRef objects
|
||||
// structure with values dumped as strings. For each trust, an extra
|
||||
// entry "SecPolicyOid" is added whose value is the OID for this trust.
|
||||
// The extra entries are used to construct trustedKeyUsageValue.
|
||||
List<Map<String, String>> trustSettings;
|
||||
|
||||
// One or more OIDs defined in http://oidref.com/1.2.840.113635.100.1.
|
||||
// It can also be "2.5.29.37.0" for a self-signed certificate with
|
||||
// an empty trust settings. This value is never empty. When there are
|
||||
// multiple OID values, it takes the form of "[1.1.1, 1.1.2]".
|
||||
String trustedKeyUsageValue;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -300,6 +319,35 @@ public final class KeychainStore extends KeyStoreSpi {
|
||||
}
|
||||
}
|
||||
|
||||
private record LocalAttr(String name, String value)
|
||||
implements KeyStore.Entry.Attribute {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
|
||||
if (engineIsCertificateEntry(alias)) {
|
||||
Object entry = entries.get(alias.toLowerCase());
|
||||
if (entry instanceof TrustedCertEntry tEntry) {
|
||||
return new KeyStore.TrustedCertificateEntry(
|
||||
tEntry.cert, Set.of(
|
||||
new LocalAttr(KnownOIDs.ORACLE_TrustedKeyUsage.value(), tEntry.trustedKeyUsageValue),
|
||||
new LocalAttr("trustSettings", tEntry.trustSettings.toString())));
|
||||
}
|
||||
}
|
||||
return super.engineGetEntry(alias, protParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the creation date of the entry identified by the given alias.
|
||||
*
|
||||
@@ -453,55 +501,12 @@ public final class KeychainStore extends KeyStoreSpi {
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the given certificate to the given alias.
|
||||
*
|
||||
* <p>If the given alias already exists in this keystore and identifies a
|
||||
* <i>trusted certificate entry</i>, the certificate associated with it is
|
||||
* overridden by the given certificate.
|
||||
*
|
||||
* @param alias the alias name
|
||||
* @param cert the certificate
|
||||
*
|
||||
* @exception KeyStoreException if the given alias already exists and does
|
||||
* not identify a <i>trusted certificate entry</i>, or this operation
|
||||
* fails for some other reason.
|
||||
* Adding trusted certificate entry is not supported.
|
||||
*/
|
||||
public void engineSetCertificateEntry(String alias, Certificate cert)
|
||||
throws KeyStoreException
|
||||
{
|
||||
permissionCheck();
|
||||
|
||||
synchronized(entries) {
|
||||
|
||||
Object entry = entries.get(alias.toLowerCase());
|
||||
if ((entry != null) && (entry instanceof KeyEntry)) {
|
||||
throw new KeyStoreException
|
||||
("Cannot overwrite key entry with certificate");
|
||||
}
|
||||
|
||||
// This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry.
|
||||
// Security framework doesn't support the same certificate twice in a keychain.
|
||||
Collection<Object> allValues = entries.values();
|
||||
|
||||
for (Object value : allValues) {
|
||||
if (value instanceof TrustedCertEntry) {
|
||||
TrustedCertEntry tce = (TrustedCertEntry)value;
|
||||
if (tce.cert.equals(cert)) {
|
||||
throw new KeyStoreException("Keychain does not support mulitple copies of same certificate.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
|
||||
trustedCertEntry.cert = cert;
|
||||
trustedCertEntry.date = new Date();
|
||||
String lowerAlias = alias.toLowerCase();
|
||||
if (entries.get(lowerAlias) != null) {
|
||||
deletedEntries.put(lowerAlias, entries.get(lowerAlias));
|
||||
}
|
||||
entries.put(lowerAlias, trustedCertEntry);
|
||||
addedEntries.put(lowerAlias, trustedCertEntry);
|
||||
}
|
||||
throws KeyStoreException {
|
||||
throw new KeyStoreException("Cannot set trusted certificate entry." +
|
||||
" Use the macOS \"security add-trusted-cert\" command instead.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -680,10 +685,7 @@ public final class KeychainStore extends KeyStoreSpi {
|
||||
String alias = e.nextElement();
|
||||
Object entry = addedEntries.get(alias);
|
||||
if (entry instanceof TrustedCertEntry) {
|
||||
TrustedCertEntry tce = (TrustedCertEntry)entry;
|
||||
Certificate certElem;
|
||||
certElem = tce.cert;
|
||||
tce.certRef = addCertificateToKeychain(alias, certElem);
|
||||
// Cannot set trusted certificate entry
|
||||
} else {
|
||||
KeyEntry keyEntry = (KeyEntry)entry;
|
||||
|
||||
@@ -778,9 +780,28 @@ public final class KeychainStore extends KeyStoreSpi {
|
||||
private native void _scanKeychain();
|
||||
|
||||
/**
|
||||
* Callback method from _scanKeychain. If a trusted certificate is found, this method will be called.
|
||||
* Callback method from _scanKeychain. If a trusted certificate is found,
|
||||
* this method will be called.
|
||||
*
|
||||
* inputTrust is a list of strings in groups. Each group contains key/value
|
||||
* pairs for one trust setting and ends with a null. Thus the size of the
|
||||
* whole list is (2 * s_1 + 1) + (2 * s_2 + 1) + ... + (2 * s_n + 1),
|
||||
* where s_i is the size of mapping for the i'th trust setting,
|
||||
* and n is the number of trust settings. Ex:
|
||||
*
|
||||
* key1 for trust1
|
||||
* value1 for trust1
|
||||
* ..
|
||||
* null (end of trust1)
|
||||
* key1 for trust2
|
||||
* value1 for trust2
|
||||
* ...
|
||||
* null (end of trust2)
|
||||
* ...
|
||||
* null (end if trust_n)
|
||||
*/
|
||||
private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) {
|
||||
private void createTrustedCertEntry(String alias, List<String> inputTrust,
|
||||
long keychainItemRef, long creationDate, byte[] derStream) {
|
||||
TrustedCertEntry tce = new TrustedCertEntry();
|
||||
|
||||
try {
|
||||
@@ -791,6 +812,69 @@ public final class KeychainStore extends KeyStoreSpi {
|
||||
tce.cert = cert;
|
||||
tce.certRef = keychainItemRef;
|
||||
|
||||
tce.trustSettings = new ArrayList<>();
|
||||
Map<String,String> tmpMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < inputTrust.size(); i++) {
|
||||
if (inputTrust.get(i) == null) {
|
||||
tce.trustSettings.add(tmpMap);
|
||||
if (i < inputTrust.size() - 1) {
|
||||
// Prepare an empty map for the next trust setting.
|
||||
// Do not just clear(), must be a new object.
|
||||
// Only create if not at end of list.
|
||||
tmpMap = new LinkedHashMap<>();
|
||||
}
|
||||
} else {
|
||||
tmpMap.put(inputTrust.get(i), inputTrust.get(i+1));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSelfSigned;
|
||||
try {
|
||||
cert.verify(cert.getPublicKey());
|
||||
isSelfSigned = true;
|
||||
} catch (Exception e) {
|
||||
isSelfSigned = false;
|
||||
}
|
||||
if (tce.trustSettings.isEmpty()) {
|
||||
if (isSelfSigned) {
|
||||
// If a self-signed certificate has an empty trust settings,
|
||||
// trust it for all purposes
|
||||
tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value();
|
||||
} else {
|
||||
// Otherwise, return immediately. The certificate is not
|
||||
// added into entries.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
List<String> values = new ArrayList<>();
|
||||
for (var oneTrust : tce.trustSettings) {
|
||||
var result = oneTrust.get("kSecTrustSettingsResult");
|
||||
// https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc
|
||||
// 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot
|
||||
// If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed
|
||||
// for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings).
|
||||
// Note that the same SecPolicyOid can appear in multiple trust settings
|
||||
// for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString.
|
||||
if ((result == null && isSelfSigned)
|
||||
|| "1".equals(result) || "2".equals(result)) {
|
||||
// When no kSecTrustSettingsPolicy, it means everything
|
||||
String oid = oneTrust.getOrDefault("SecPolicyOid",
|
||||
KnownOIDs.anyExtendedKeyUsage.value());
|
||||
if (!values.contains(oid)) {
|
||||
values.add(oid);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (values.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (values.size() == 1) {
|
||||
tce.trustedKeyUsageValue = values.get(0);
|
||||
} else {
|
||||
tce.trustedKeyUsageValue = values.toString();
|
||||
}
|
||||
}
|
||||
// Make a creation date.
|
||||
if (creationDate != 0)
|
||||
tce.date = new Date(creationDate);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@@ -368,6 +368,14 @@ errOut:
|
||||
}
|
||||
}
|
||||
|
||||
#define ADD(list, str) { \
|
||||
jobject localeObj = (*env)->NewStringUTF(env, [str UTF8String]); \
|
||||
(*env)->CallBooleanMethod(env, list, jm_listAdd, localeObj); \
|
||||
(*env)->DeleteLocalRef(env, localeObj); \
|
||||
}
|
||||
|
||||
#define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL)
|
||||
|
||||
static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
|
||||
{
|
||||
// Search the user keychain list for all X509 certificates.
|
||||
@@ -379,8 +387,15 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
|
||||
jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore");
|
||||
CHECK_NULL(jc_KeychainStore);
|
||||
jmethodID jm_createTrustedCertEntry = (*env)->GetMethodID(
|
||||
env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V");
|
||||
env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;Ljava/util/List;JJ[B)V");
|
||||
CHECK_NULL(jm_createTrustedCertEntry);
|
||||
jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
|
||||
CHECK_NULL(jc_arrayListClass);
|
||||
jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "<init>", "()V");
|
||||
CHECK_NULL(jm_arrayListCons);
|
||||
jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z");
|
||||
CHECK_NULL(jm_listAdd);
|
||||
|
||||
do {
|
||||
searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem);
|
||||
|
||||
@@ -401,12 +416,50 @@ static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore)
|
||||
goto errOut;
|
||||
}
|
||||
|
||||
// Only add certificates with trusted settings
|
||||
CFArrayRef trustSettings;
|
||||
if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings)
|
||||
== errSecItemNotFound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// See KeychainStore::createTrustedCertEntry for content of inputTrust
|
||||
jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons);
|
||||
CHECK_NULL(inputTrust);
|
||||
|
||||
// Dump everything inside trustSettings into inputTrust
|
||||
CFIndex count = CFArrayGetCount(trustSettings);
|
||||
for (int i = 0; i < count; i++) {
|
||||
CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i);
|
||||
CFIndex size = CFDictionaryGetCount(oneTrust);
|
||||
const void * keys [size];
|
||||
const void * values [size];
|
||||
CFDictionaryGetKeysAndValues(oneTrust, keys, values);
|
||||
for (int j = 0; j < size; j++) {
|
||||
NSString* s = [NSString stringWithFormat:@"%@", keys[j]];
|
||||
ADD(inputTrust, s);
|
||||
s = [NSString stringWithFormat:@"%@", values[j]];
|
||||
ADD(inputTrust, s);
|
||||
}
|
||||
SecPolicyRef certPolicy;
|
||||
certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy);
|
||||
if (certPolicy != NULL) {
|
||||
CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy);
|
||||
ADD(inputTrust, @"SecPolicyOid");
|
||||
NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")];
|
||||
ADD(inputTrust, s);
|
||||
CFRelease(policyDict);
|
||||
}
|
||||
ADDNULL(inputTrust);
|
||||
}
|
||||
CFRelease(trustSettings);
|
||||
|
||||
// Find the creation date.
|
||||
jlong creationDate = getModDateFromItem(env, theItem);
|
||||
|
||||
// Call back to the Java object to create Java objects corresponding to this security object.
|
||||
jlong nativeRef = ptr_to_jlong(certRef);
|
||||
(*env)->CallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData);
|
||||
(*env)->CallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, inputTrust, nativeRef, creationDate, certData);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
} while (searchResult == noErr);
|
||||
@@ -522,8 +575,8 @@ NSString* JavaStringToNSString(JNIEnv *env, jstring jstr) {
|
||||
/*
|
||||
* Class: apple_security_KeychainStore
|
||||
* Method: _addItemToKeychain
|
||||
* Signature: (Ljava/lang/String;[B)I
|
||||
*/
|
||||
* Signature: (Ljava/lang/String;Z[B[C)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain
|
||||
(JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@@ -96,6 +96,10 @@ class JarVerifier {
|
||||
/** collect -DIGEST-MANIFEST values for deny list */
|
||||
private List<Object> manifestDigests;
|
||||
|
||||
/* A cache mapping code signers to the algorithms used to digest jar
|
||||
entries, and whether or not the algorithms are permitted. */
|
||||
private Map<CodeSigner[], Map<String, Boolean>> signersToAlgs;
|
||||
|
||||
public JarVerifier(String name, byte[] rawBytes) {
|
||||
manifestName = name;
|
||||
manifestRawBytes = rawBytes;
|
||||
@@ -105,6 +109,7 @@ class JarVerifier {
|
||||
pendingBlocks = new ArrayList<>();
|
||||
baos = new ByteArrayOutputStream();
|
||||
manifestDigests = new ArrayList<>();
|
||||
signersToAlgs = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,7 +249,8 @@ class JarVerifier {
|
||||
if (!parsingBlockOrSF) {
|
||||
JarEntry je = mev.getEntry();
|
||||
if ((je != null) && (je.signers == null)) {
|
||||
je.signers = mev.verify(verifiedSigners, sigFileSigners);
|
||||
je.signers = mev.verify(verifiedSigners, sigFileSigners,
|
||||
signersToAlgs);
|
||||
je.certs = mapSignersToCertArray(je.signers);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -658,8 +658,8 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
if (!(memberType.isInstance(value) ||
|
||||
value instanceof ExceptionProxy)) {
|
||||
value = new AnnotationTypeMismatchExceptionProxy(
|
||||
value.getClass() + "[" + value + "]").setMember(
|
||||
annotationType.members().get(name));
|
||||
objectToString(value))
|
||||
.setMember(annotationType.members().get(name));
|
||||
}
|
||||
}
|
||||
mv.put(name, value);
|
||||
@@ -669,6 +669,15 @@ class AnnotationInvocationHandler implements InvocationHandler, Serializable {
|
||||
UnsafeAccessor.setMemberValues(this, mv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a textual representation of the argument without calling
|
||||
* any overridable methods of the argument.
|
||||
*/
|
||||
private static String objectToString(Object value) {
|
||||
return value.getClass().getName() + "@" +
|
||||
Integer.toHexString(System.identityHashCode(value));
|
||||
}
|
||||
|
||||
private static class UnsafeAccessor {
|
||||
private static final jdk.internal.misc.Unsafe unsafe
|
||||
= jdk.internal.misc.Unsafe.getUnsafe();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2021, 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
|
||||
@@ -362,7 +362,8 @@ abstract class DSA extends SignatureSpi {
|
||||
s = new BigInteger(1, s.toByteArray());
|
||||
}
|
||||
|
||||
if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) {
|
||||
if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)
|
||||
&& r.signum() > 0 && s.signum() > 0) {
|
||||
BigInteger w = generateW(presetP, presetQ, presetG, s);
|
||||
BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r);
|
||||
return v.equals(r);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@@ -2209,6 +2209,9 @@ public final class Main {
|
||||
out.println(mf);
|
||||
dumpCert(cert, out);
|
||||
} else if (debug) {
|
||||
for (var attr : keyStore.getEntry(alias, null).getAttributes()) {
|
||||
System.out.println("Attribute " + attr.getName() + ": " + attr.getValue());
|
||||
}
|
||||
out.println(cert.toString());
|
||||
} else {
|
||||
out.println("trustedCertEntry, ");
|
||||
|
||||
@@ -144,15 +144,14 @@ class DerIndefLenConverter {
|
||||
* then skip the tag and its 1 byte length of zero.
|
||||
*/
|
||||
private void writeTag() {
|
||||
if (dataPos == dataSize) {
|
||||
return;
|
||||
}
|
||||
assert dataPos + 1 < dataSize;
|
||||
if (isEOC(data, dataPos)) {
|
||||
dataPos += 2; // skip tag and length
|
||||
writeTag();
|
||||
} else {
|
||||
newData[newDataPos++] = data[dataPos++];
|
||||
while (dataPos < dataSize) {
|
||||
assert dataPos + 1 < dataSize;
|
||||
if (isEOC(data, dataPos)) {
|
||||
dataPos += 2; // skip tag and length
|
||||
} else {
|
||||
newData[newDataPos++] = data[dataPos++];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@@ -192,7 +192,8 @@ public class ManifestEntryVerifier {
|
||||
*
|
||||
*/
|
||||
public CodeSigner[] verify(Hashtable<String, CodeSigner[]> verifiedSigners,
|
||||
Hashtable<String, CodeSigner[]> sigFileSigners)
|
||||
Hashtable<String, CodeSigner[]> sigFileSigners,
|
||||
Map<CodeSigner[], Map<String, Boolean>> signersToAlgs)
|
||||
throws JarException
|
||||
{
|
||||
if (skip) {
|
||||
@@ -207,38 +208,60 @@ public class ManifestEntryVerifier {
|
||||
return signers;
|
||||
}
|
||||
|
||||
JarConstraintsParameters params =
|
||||
getParams(verifiedSigners, sigFileSigners);
|
||||
CodeSigner[] entrySigners = sigFileSigners.get(name);
|
||||
Map<String, Boolean> algsPermittedStatus =
|
||||
algsPermittedStatusForSigners(signersToAlgs, entrySigners);
|
||||
|
||||
// Flag that indicates if only disabled algorithms are used and jar
|
||||
// entry should be treated as unsigned.
|
||||
boolean disabledAlgs = true;
|
||||
JarConstraintsParameters params = null;
|
||||
for (int i=0; i < digests.size(); i++) {
|
||||
MessageDigest digest = digests.get(i);
|
||||
if (params != null) {
|
||||
try {
|
||||
params.setExtendedExceptionMsg(JarFile.MANIFEST_NAME,
|
||||
name + " entry");
|
||||
DisabledAlgorithmConstraints.jarConstraints()
|
||||
.permits(digest.getAlgorithm(), params, false);
|
||||
} catch (GeneralSecurityException e) {
|
||||
if (debug != null) {
|
||||
debug.println("Digest algorithm is restricted: " + e);
|
||||
String digestAlg = digest.getAlgorithm();
|
||||
|
||||
// Check if this algorithm is permitted, skip if false.
|
||||
if (algsPermittedStatus != null) {
|
||||
Boolean permitted = algsPermittedStatus.get(digestAlg);
|
||||
if (permitted == null) {
|
||||
if (params == null) {
|
||||
params = new JarConstraintsParameters(entrySigners);
|
||||
}
|
||||
return null;
|
||||
if (!checkConstraints(digestAlg, params)) {
|
||||
algsPermittedStatus.put(digestAlg, Boolean.FALSE);
|
||||
continue;
|
||||
} else {
|
||||
algsPermittedStatus.put(digestAlg, Boolean.TRUE);
|
||||
}
|
||||
} else if (!permitted) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// A non-disabled algorithm was used.
|
||||
disabledAlgs = false;
|
||||
|
||||
byte [] manHash = manifestHashes.get(i);
|
||||
byte [] theHash = digest.digest();
|
||||
|
||||
if (debug != null) {
|
||||
debug.println("Manifest Entry: " +
|
||||
name + " digest=" + digest.getAlgorithm());
|
||||
name + " digest=" + digestAlg);
|
||||
debug.println(" manifest " + HexFormat.of().formatHex(manHash));
|
||||
debug.println(" computed " + HexFormat.of().formatHex(theHash));
|
||||
debug.println();
|
||||
}
|
||||
|
||||
if (!MessageDigest.isEqual(theHash, manHash))
|
||||
throw new SecurityException(digest.getAlgorithm()+
|
||||
if (!MessageDigest.isEqual(theHash, manHash)) {
|
||||
throw new SecurityException(digestAlg +
|
||||
" digest error for "+name);
|
||||
}
|
||||
}
|
||||
|
||||
// If there were only disabled algorithms used, return null and jar
|
||||
// entry will be treated as unsigned.
|
||||
if (disabledAlgs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// take it out of sigFileSigners and put it in verifiedSigners...
|
||||
@@ -249,40 +272,36 @@ public class ManifestEntryVerifier {
|
||||
return signers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get constraints parameters for JAR. The constraints should be
|
||||
* checked against all code signers. Returns the parameters,
|
||||
* or null if the signers for this entry have already been checked
|
||||
* or there are no signers for this entry.
|
||||
*/
|
||||
private JarConstraintsParameters getParams(
|
||||
Map<String, CodeSigner[]> verifiedSigners,
|
||||
Map<String, CodeSigner[]> sigFileSigners) {
|
||||
// Gets the algorithms permitted status for the signers of this entry.
|
||||
private static Map<String, Boolean> algsPermittedStatusForSigners(
|
||||
Map<CodeSigner[], Map<String, Boolean>> signersToAlgs,
|
||||
CodeSigner[] signers) {
|
||||
if (signers != null) {
|
||||
Map<String, Boolean> algs = signersToAlgs.get(signers);
|
||||
// create new HashMap if absent
|
||||
if (algs == null) {
|
||||
algs = new HashMap<>();
|
||||
signersToAlgs.put(signers, algs);
|
||||
}
|
||||
return algs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// verifiedSigners is usually preloaded with the Manifest's signers.
|
||||
// If verifiedSigners contains the Manifest, then it will have all of
|
||||
// the signers of the JAR. But if it doesn't then we need to fallback
|
||||
// and check verifiedSigners to see if the signers of this entry have
|
||||
// been checked already.
|
||||
if (verifiedSigners.containsKey(manifestFileName)) {
|
||||
if (verifiedSigners.size() > 1) {
|
||||
// this means we already checked it previously
|
||||
return null;
|
||||
} else {
|
||||
return new JarConstraintsParameters(
|
||||
verifiedSigners.get(manifestFileName));
|
||||
}
|
||||
} else {
|
||||
// Checks the algorithm constraints against the signers of this entry.
|
||||
private boolean checkConstraints(String algorithm,
|
||||
JarConstraintsParameters params) {
|
||||
try {
|
||||
params.setExtendedExceptionMsg(JarFile.MANIFEST_NAME,
|
||||
name + " entry");
|
||||
DisabledAlgorithmConstraints.jarConstraints()
|
||||
.permits(algorithm, params, false);
|
||||
return true;
|
||||
} catch (GeneralSecurityException e) {
|
||||
if (debug != null) {
|
||||
debug.println(manifestFileName + " not present in verifiedSigners");
|
||||
}
|
||||
CodeSigner[] signers = sigFileSigners.get(name);
|
||||
if (signers == null || verifiedSigners.containsValue(signers)) {
|
||||
return null;
|
||||
} else {
|
||||
return new JarConstraintsParameters(signers);
|
||||
debug.println("Digest algorithm is restricted: " + e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2021, 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
|
||||
@@ -365,7 +365,7 @@ public final class ObjectIdentifier implements Serializable {
|
||||
if ((encoding[i] & 0x80) == 0) {
|
||||
// one section [fromPos..i]
|
||||
if (i - fromPos + 1 > 4) {
|
||||
BigInteger big = new BigInteger(pack(encoding,
|
||||
BigInteger big = new BigInteger(1, pack(encoding,
|
||||
fromPos, i-fromPos+1, 7, 8));
|
||||
if (fromPos == 0) {
|
||||
result[which++] = 2;
|
||||
@@ -434,7 +434,7 @@ public final class ObjectIdentifier implements Serializable {
|
||||
sb.append('.');
|
||||
}
|
||||
if (i - fromPos + 1 > 4) { // maybe big integer
|
||||
BigInteger big = new BigInteger(
|
||||
BigInteger big = new BigInteger(1,
|
||||
pack(encoding, fromPos, i-fromPos+1, 7, 8));
|
||||
if (fromPos == 0) {
|
||||
// first section encoded with more than 4 bytes,
|
||||
@@ -684,6 +684,10 @@ public final class ObjectIdentifier implements Serializable {
|
||||
}
|
||||
|
||||
private static void checkOidSize(int oidLength) throws IOException {
|
||||
if (oidLength < 0) {
|
||||
throw new IOException("ObjectIdentifier encoded length was " +
|
||||
"negative: " + oidLength);
|
||||
}
|
||||
if (oidLength > MAXIMUM_OID_SIZE) {
|
||||
throw new IOException(
|
||||
"ObjectIdentifier encoded length exceeds " +
|
||||
|
||||
@@ -104,7 +104,6 @@ static int createProxyList(LPWSTR win_proxy, const WCHAR *pproto, list_item **he
|
||||
int nr_elems = 0;
|
||||
wchar_t *context = NULL;
|
||||
wchar_t *current_proxy = NULL;
|
||||
BOOL error = FALSE;
|
||||
|
||||
/*
|
||||
* The proxy server list contains one or more of the following strings
|
||||
@@ -116,7 +115,6 @@ static int createProxyList(LPWSTR win_proxy, const WCHAR *pproto, list_item **he
|
||||
LPWSTR pport;
|
||||
LPWSTR phost;
|
||||
int portVal = 0;
|
||||
wchar_t *next_proxy = NULL;
|
||||
list_item *proxy = NULL;
|
||||
wchar_t* pos = NULL;
|
||||
|
||||
@@ -290,7 +288,6 @@ Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
|
||||
}
|
||||
|
||||
if (win_proxy != NULL) {
|
||||
wchar_t *context = NULL;
|
||||
int defport = 0;
|
||||
int nr_elems = 0;
|
||||
|
||||
@@ -313,27 +310,28 @@ Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
|
||||
nr_elems = createProxyList(win_proxy, lpProto, &head);
|
||||
if (nr_elems != 0 && head != NULL) {
|
||||
int index = 0;
|
||||
list_item *current = head;
|
||||
proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL);
|
||||
if (proxy_array == NULL || (*env)->ExceptionCheck(env)) {
|
||||
goto noproxy;
|
||||
}
|
||||
while (head != NULL && index < nr_elems) {
|
||||
while (current != NULL && index < nr_elems) {
|
||||
jstring jhost;
|
||||
jobject isa;
|
||||
jobject proxy;
|
||||
|
||||
if (head->host != NULL && proxy_array != NULL) {
|
||||
if (current->host != NULL && proxy_array != NULL) {
|
||||
/* Let's create the appropriate Proxy object then. */
|
||||
if (head->port == 0) {
|
||||
head->port = defport;
|
||||
if (current->port == 0) {
|
||||
current->port = defport;
|
||||
}
|
||||
jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host));
|
||||
jhost = (*env)->NewString(env, current->host, (jsize)wcslen(current->host));
|
||||
if (jhost == NULL || (*env)->ExceptionCheck(env)) {
|
||||
proxy_array = NULL;
|
||||
}
|
||||
isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
|
||||
isaddr_createUnresolvedID, jhost,
|
||||
head->port);
|
||||
current->port);
|
||||
if (isa == NULL || (*env)->ExceptionCheck(env)) {
|
||||
proxy_array = NULL;
|
||||
}
|
||||
@@ -347,7 +345,7 @@ Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
|
||||
}
|
||||
index++;
|
||||
}
|
||||
head = head->next;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.sun.imageio.plugins.common.I18N;
|
||||
import com.sun.imageio.plugins.common.ImageUtil;
|
||||
import com.sun.imageio.plugins.common.ReaderUtil;
|
||||
|
||||
/** This class is the Java Image IO plugin reader for BMP images.
|
||||
* It may subsample the image, clip the image, select sub-bands,
|
||||
@@ -1542,9 +1543,8 @@ public class BMPImageReader extends ImageReader implements BMPConstants {
|
||||
}
|
||||
|
||||
// Read till we have the whole image
|
||||
byte[] values = new byte[imSize];
|
||||
int bytesRead = 0;
|
||||
iis.readFully(values, 0, imSize);
|
||||
byte[] values = ReaderUtil.
|
||||
staggeredReadByteStream(iis, imSize);
|
||||
|
||||
// Since data is compressed, decompress it
|
||||
decodeRLE8(imSize, padding, values, bdata);
|
||||
@@ -1726,8 +1726,8 @@ public class BMPImageReader extends ImageReader implements BMPConstants {
|
||||
}
|
||||
|
||||
// Read till we have the whole image
|
||||
byte[] values = new byte[imSize];
|
||||
iis.readFully(values, 0, imSize);
|
||||
byte[] values = ReaderUtil.
|
||||
staggeredReadByteStream(iis, imSize);
|
||||
|
||||
// Decompress the RLE4 compressed data.
|
||||
decodeRLE4(imSize, padding, values, bdata);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2022, 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2007, 2021, 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
|
||||
@@ -78,12 +78,26 @@ public final class AudioFileSoundbankReader extends SoundbankReader {
|
||||
|
||||
public Soundbank getSoundbank(AudioInputStream ais)
|
||||
throws InvalidMidiDataException, IOException {
|
||||
int MEGABYTE = 1048576;
|
||||
int DEFAULT_BUFFER_SIZE = 65536;
|
||||
int MAX_FRAME_SIZE = 1024;
|
||||
try {
|
||||
byte[] buffer;
|
||||
if (ais.getFrameLength() == -1) {
|
||||
int frameSize = ais.getFormat().getFrameSize();
|
||||
if (frameSize <= 0 || frameSize > MAX_FRAME_SIZE) {
|
||||
throw new InvalidMidiDataException("Formats with frame size "
|
||||
+ frameSize + " are not supported");
|
||||
}
|
||||
|
||||
long totalSize = ais.getFrameLength() * frameSize;
|
||||
if (totalSize >= Integer.MAX_VALUE - 2) {
|
||||
throw new InvalidMidiDataException(
|
||||
"Can not allocate enough memory to read audio data.");
|
||||
}
|
||||
|
||||
if (ais.getFrameLength() == -1 || totalSize > MEGABYTE) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buff = new byte[1024
|
||||
- (1024 % ais.getFormat().getFrameSize())];
|
||||
byte[] buff = new byte[DEFAULT_BUFFER_SIZE - (DEFAULT_BUFFER_SIZE % frameSize)];
|
||||
int ret;
|
||||
while ((ret = ais.read(buff)) != -1) {
|
||||
baos.write(buff, 0, ret);
|
||||
@@ -91,8 +105,7 @@ public final class AudioFileSoundbankReader extends SoundbankReader {
|
||||
ais.close();
|
||||
buffer = baos.toByteArray();
|
||||
} else {
|
||||
buffer = new byte[(int) (ais.getFrameLength()
|
||||
* ais.getFormat().getFrameSize())];
|
||||
buffer = new byte[(int) totalSize];
|
||||
new DataInputStream(ais).readFully(buffer);
|
||||
}
|
||||
ModelByteBufferWavetable osc = new ModelByteBufferWavetable(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, 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
|
||||
@@ -3917,11 +3917,11 @@ void AwtComponent::SetCandidateWindow(int iCandType, int x, int y)
|
||||
HIMC hIMC = ImmGetContext(hwnd);
|
||||
if (hIMC) {
|
||||
CANDIDATEFORM cf;
|
||||
cf.dwStyle = CFS_POINT;
|
||||
cf.dwStyle = CFS_CANDIDATEPOS;
|
||||
ImmGetCandidateWindow(hIMC, 0, &cf);
|
||||
if (x != cf.ptCurrentPos.x || y != cf.ptCurrentPos.y) {
|
||||
cf.dwIndex = iCandType;
|
||||
cf.dwStyle = CFS_POINT;
|
||||
cf.dwStyle = CFS_CANDIDATEPOS;
|
||||
cf.ptCurrentPos = {x, y};
|
||||
cf.rcArea = {0, 0, 0, 0};
|
||||
ImmSetCandidateWindow(hIMC, &cf);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@@ -267,13 +267,13 @@ void AwtDragSource::_DoDragDrop(void* param) {
|
||||
dragSource->Signal();
|
||||
|
||||
AwtToolkit &toolkit = AwtToolkit::GetInstance();
|
||||
toolkit.isInDoDragDropLoop = TRUE;
|
||||
toolkit.isDnDSourceActive = TRUE;
|
||||
res = ::DoDragDrop(dragSource,
|
||||
dragSource,
|
||||
convertActionsToDROPEFFECT(dragSource->m_actions),
|
||||
&effects
|
||||
);
|
||||
toolkit.isInDoDragDropLoop = FALSE;
|
||||
toolkit.isDnDSourceActive = FALSE;
|
||||
|
||||
if (effects == DROPEFFECT_NONE && dragSource->m_dwPerformedDropEffect != DROPEFFECT_NONE) {
|
||||
effects = dragSource->m_dwPerformedDropEffect;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@@ -141,7 +141,7 @@ static void ScaleDown(POINT &cp, HWND m_window) {
|
||||
|
||||
HRESULT __stdcall AwtDropTarget::DragEnter(IDataObject __RPC_FAR *pDataObj, DWORD grfKeyState, POINTL pt, DWORD __RPC_FAR *pdwEffect) {
|
||||
TRY;
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = TRUE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = TRUE;
|
||||
if (NULL != m_pIDropTargetHelper) {
|
||||
m_pIDropTargetHelper->DragEnter(
|
||||
m_window,
|
||||
@@ -161,7 +161,7 @@ HRESULT __stdcall AwtDropTarget::DragEnter(IDataObject __RPC_FAR *pDataObj, DWOR
|
||||
(IsLocalDnD() && !IsLocalDataObject(pDataObj)))
|
||||
{
|
||||
*pdwEffect = retEffect;
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = FALSE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = FALSE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ HRESULT __stdcall AwtDropTarget::DragEnter(IDataObject __RPC_FAR *pDataObj, DWOR
|
||||
}
|
||||
|
||||
if (JNU_IsNull(env, m_dtcp) || !JNU_IsNull(env, safe_ExceptionOccurred(env))) {
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = FALSE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = FALSE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -200,12 +200,12 @@ HRESULT __stdcall AwtDropTarget::DragEnter(IDataObject __RPC_FAR *pDataObj, DWOR
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
actions = java_awt_dnd_DnDConstants_ACTION_NONE;
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = FALSE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = FALSE;
|
||||
}
|
||||
} catch (std::bad_alloc&) {
|
||||
retEffect = ::convertActionsToDROPEFFECT(actions);
|
||||
*pdwEffect = retEffect;
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = FALSE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = FALSE;
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ void AwtDropTarget::DropDone(jboolean success, jint action) {
|
||||
m_dropSuccess = success;
|
||||
m_dropActions = action;
|
||||
AwtToolkit::GetInstance().QuitMessageLoop(AwtToolkit::EXIT_ENCLOSING_LOOP);
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = FALSE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1136,7 +1136,7 @@ void AwtDropTarget::UnloadCache() {
|
||||
|
||||
void AwtDropTarget::DragCleanup(void) {
|
||||
UnloadCache();
|
||||
AwtToolkit::GetInstance().isInDoDragDropLoop = FALSE;
|
||||
AwtToolkit::GetInstance().isDnDTargetActive = FALSE;
|
||||
}
|
||||
|
||||
BOOL AwtDropTarget::IsLocalDataObject(IDataObject __RPC_FAR *pDataObject) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, 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
|
||||
@@ -344,7 +344,8 @@ AwtToolkit::AwtToolkit() {
|
||||
|
||||
m_waitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
m_inputMethodWaitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
isInDoDragDropLoop = FALSE;
|
||||
isDnDSourceActive = FALSE;
|
||||
isDnDTargetActive = FALSE;
|
||||
eventNumber = 0;
|
||||
}
|
||||
|
||||
@@ -3012,7 +3013,7 @@ Java_sun_awt_windows_WToolkit_syncNativeQueue(JNIEnv *env, jobject self, jlong t
|
||||
tk.PostMessage(WM_SYNC_WAIT, 0, 0);
|
||||
for(long t = 2; t < timeout &&
|
||||
WAIT_TIMEOUT == ::WaitForSingleObject(tk.m_waitEvent, 2); t+=2) {
|
||||
if (tk.isInDoDragDropLoop) {
|
||||
if (tk.isDnDSourceActive || tk.isDnDTargetActive) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3216,7 +3217,7 @@ LRESULT AwtToolkit::InvokeInputMethodFunction(UINT msg, WPARAM wParam, LPARAM lP
|
||||
* the IME completion.
|
||||
*/
|
||||
CriticalSection::Lock lock(m_inputMethodLock);
|
||||
if (isInDoDragDropLoop) {
|
||||
if (isDnDSourceActive || isDnDTargetActive) {
|
||||
SendMessage(msg, wParam, lParam);
|
||||
::ResetEvent(m_inputMethodWaitEvent);
|
||||
return m_inputMethodData;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2022, 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
|
||||
@@ -441,7 +441,8 @@ public:
|
||||
|
||||
HANDLE m_waitEvent;
|
||||
volatile DWORD eventNumber;
|
||||
volatile BOOL isInDoDragDropLoop;
|
||||
volatile BOOL isDnDSourceActive;
|
||||
volatile BOOL isDnDTargetActive;
|
||||
private:
|
||||
HWND CreateToolkitWnd(LPCTSTR name);
|
||||
|
||||
|
||||
@@ -65,7 +65,23 @@ final class LdapClientFactory implements PooledConnectionFactory {
|
||||
connTimeout, readTimeout, trace, pcb);
|
||||
}
|
||||
|
||||
public PooledConnection createPooledConnection(PoolCallback pcb, long timeout)
|
||||
throws NamingException {
|
||||
return new LdapClient(host, port, socketFactory,
|
||||
guardedIntegerCast(timeout),
|
||||
readTimeout, trace, pcb);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return host + ":" + port;
|
||||
}
|
||||
|
||||
private int guardedIntegerCast(long timeout) {
|
||||
if (timeout < Integer.MIN_VALUE) {
|
||||
return Integer.MIN_VALUE;
|
||||
} else if (timeout > Integer.MAX_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return (int) timeout;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@@ -28,6 +28,10 @@ package com.sun.jndi.ldap;
|
||||
import javax.naming.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
import com.sun.jndi.toolkit.url.Uri;
|
||||
import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
@@ -64,6 +68,25 @@ import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
|
||||
public final class LdapURL extends Uri {
|
||||
|
||||
private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing";
|
||||
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
||||
|
||||
public static final ParseMode PARSE_MODE;
|
||||
static {
|
||||
PrivilegedAction<String> action = () ->
|
||||
System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
||||
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
||||
try {
|
||||
@SuppressWarnings("removal")
|
||||
String mode = AccessController.doPrivileged(action);
|
||||
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
||||
} catch (Throwable t) {
|
||||
parseMode = DEFAULT_PARSE_MODE;
|
||||
} finally {
|
||||
PARSE_MODE = parseMode;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useSsl = false;
|
||||
private String DN = null;
|
||||
private String attributes = null;
|
||||
@@ -83,7 +106,7 @@ public final class LdapURL extends Uri {
|
||||
useSsl = scheme.equalsIgnoreCase("ldaps");
|
||||
|
||||
if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) {
|
||||
throw new MalformedURLException("Not an LDAP URL: " + url);
|
||||
throw newInvalidURISchemeException(url);
|
||||
}
|
||||
|
||||
parsePathAndQuery(); // DN, attributes, scope, filter, extensions
|
||||
@@ -99,6 +122,21 @@ public final class LdapURL extends Uri {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MalformedURLException newInvalidURISchemeException(String uri) {
|
||||
return new MalformedURLException("Not an LDAP URL: " + uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSchemeOnly(String uri) {
|
||||
return isLdapSchemeOnly(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ParseMode parseMode() {
|
||||
return PARSE_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the URL is an LDAPS URL.
|
||||
*/
|
||||
@@ -151,13 +189,33 @@ public final class LdapURL extends Uri {
|
||||
StringTokenizer st = new StringTokenizer(urlList, " ");
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
urls[i++] = st.nextToken();
|
||||
// we don't accept scheme-only URLs here
|
||||
urls[i++] = validateURI(st.nextToken());
|
||||
}
|
||||
String[] trimmed = new String[i];
|
||||
System.arraycopy(urls, 0, trimmed, 0, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
public static boolean isLdapSchemeOnly(String uri) {
|
||||
return "ldap:".equals(uri) || "ldaps:".equals(uri);
|
||||
}
|
||||
|
||||
public static String validateURI(String uri) {
|
||||
// no validation in legacy mode parsing
|
||||
if (PARSE_MODE == ParseMode.LEGACY) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// special case of scheme-only URIs
|
||||
if (isLdapSchemeOnly(uri)) {
|
||||
return uri;
|
||||
}
|
||||
|
||||
// use java.net.URI to validate the uri syntax
|
||||
return URI.create(uri).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an LDAP URL has query components.
|
||||
*/
|
||||
@@ -181,7 +239,8 @@ public final class LdapURL extends Uri {
|
||||
String p = (port != -1) ? (":" + port) : "";
|
||||
String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : "";
|
||||
|
||||
return useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;
|
||||
String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d;
|
||||
return validateURI(uri);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// UTF8 should always be supported
|
||||
throw new IllegalStateException("UTF-8 encoding unavailable");
|
||||
|
||||
@@ -27,6 +27,9 @@ package com.sun.jndi.ldap.pool;
|
||||
|
||||
import java.util.ArrayList; // JDK 1.2
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
@@ -68,13 +71,19 @@ final class Connections implements PoolCallback {
|
||||
com.sun.jndi.ldap.LdapPoolManager.trace;
|
||||
private static final int DEFAULT_SIZE = 10;
|
||||
|
||||
final private int initSize;
|
||||
private final int maxSize;
|
||||
private final int prefSize;
|
||||
private final List<ConnectionDesc> conns;
|
||||
final private PooledConnectionFactory factory;
|
||||
|
||||
private boolean closed = false; // Closed for business
|
||||
private Reference<Object> ref; // maintains reference to id to prevent premature GC
|
||||
|
||||
private boolean initialized = false;
|
||||
private final ReentrantLock lock;
|
||||
private final Condition connectionsAvailable;
|
||||
|
||||
/**
|
||||
* @param id the identity (connection request) of the connections in the list
|
||||
* @param initSize the number of connections to create initially
|
||||
@@ -87,105 +96,73 @@ final class Connections implements PoolCallback {
|
||||
* when one is removed.
|
||||
* @param factory The factory responsible for creating a connection
|
||||
*/
|
||||
Connections(Object id, int initSize, int prefSize, int maxSize,
|
||||
PooledConnectionFactory factory) throws NamingException {
|
||||
|
||||
Connections(Object id, int initSize, int prefSize, int maxSize, PooledConnectionFactory factory,
|
||||
ReentrantLock lock) throws NamingException {
|
||||
this.maxSize = maxSize;
|
||||
this.lock = lock;
|
||||
this.connectionsAvailable = lock.newCondition();
|
||||
this.factory = factory;
|
||||
|
||||
if (maxSize > 0) {
|
||||
// prefSize and initSize cannot exceed specified maxSize
|
||||
this.prefSize = Math.min(prefSize, maxSize);
|
||||
initSize = Math.min(initSize, maxSize);
|
||||
this.initSize = Math.min(initSize, maxSize);
|
||||
} else {
|
||||
this.prefSize = prefSize;
|
||||
this.initSize = initSize;
|
||||
}
|
||||
conns = new ArrayList<>(maxSize > 0 ? maxSize : DEFAULT_SIZE);
|
||||
this.conns = new ArrayList<>(maxSize > 0 ? maxSize : DEFAULT_SIZE);
|
||||
this.initialized = initSize <= 0;
|
||||
|
||||
// Maintain soft ref to id so that this Connections' entry in
|
||||
// Pool doesn't get GC'ed prematurely
|
||||
ref = new SoftReference<>(id);
|
||||
this.ref = new SoftReference<>(id);
|
||||
|
||||
d("init size=", initSize);
|
||||
d("max size=", maxSize);
|
||||
d("preferred size=", prefSize);
|
||||
}
|
||||
|
||||
// Create initial connections
|
||||
PooledConnection conn;
|
||||
for (int i = 0; i < initSize; i++) {
|
||||
conn = factory.createPooledConnection(this);
|
||||
td("Create ", conn ,factory);
|
||||
conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool
|
||||
void waitForAvailableConnection() throws InterruptedNamingException {
|
||||
try {
|
||||
d("get(): waiting");
|
||||
connectionsAvailable.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new InterruptedNamingException(
|
||||
"Interrupted while waiting for a connection");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a PooledConnection from this list of connections.
|
||||
* Use an existing one if one is idle, or create one if the list's
|
||||
* max size hasn't been reached. If max size has been reached, wait
|
||||
* for a PooledConnection to be returned, or one to be removed (thus
|
||||
* not reaching the max size any longer).
|
||||
*
|
||||
* @param timeout if > 0, msec to wait until connection is available
|
||||
* @param factory creates the PooledConnection if one needs to be created
|
||||
*
|
||||
* @return A non-null PooledConnection
|
||||
* @throws NamingException PooledConnection cannot be created, because this
|
||||
* thread was interrupted while it waited for an available connection,
|
||||
* or if it timed out while waiting, or the creation of a connection
|
||||
* resulted in an error.
|
||||
*/
|
||||
synchronized PooledConnection get(long timeout,
|
||||
PooledConnectionFactory factory) throws NamingException {
|
||||
PooledConnection conn;
|
||||
long start = (timeout > 0 ? System.currentTimeMillis() : 0);
|
||||
long waittime = timeout;
|
||||
|
||||
d("get(): before");
|
||||
while ((conn = getOrCreateConnection(factory)) == null) {
|
||||
if (timeout > 0 && waittime <= 0) {
|
||||
throw new CommunicationException(
|
||||
"Timeout exceeded while waiting for a connection: " +
|
||||
timeout + "ms");
|
||||
}
|
||||
try {
|
||||
d("get(): waiting");
|
||||
if (waittime > 0) {
|
||||
wait(waittime); // Wait until one is released or removed
|
||||
} else {
|
||||
wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new InterruptedNamingException(
|
||||
void waitForAvailableConnection(long waitTime) throws InterruptedNamingException {
|
||||
try {
|
||||
d("get(): waiting");
|
||||
connectionsAvailable.await(waitTime, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new InterruptedNamingException(
|
||||
"Interrupted while waiting for a connection");
|
||||
}
|
||||
// Check whether we timed out
|
||||
if (timeout > 0) {
|
||||
long now = System.currentTimeMillis();
|
||||
waittime = timeout - (now - start);
|
||||
}
|
||||
}
|
||||
|
||||
d("get(): after");
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an idle connection from this list if one is available.
|
||||
* If none is available, create a new one if maxSize hasn't been reached.
|
||||
* If maxSize has been reached, return null.
|
||||
* Always called from a synchronized method.
|
||||
*/
|
||||
private PooledConnection getOrCreateConnection(
|
||||
PooledConnectionFactory factory) throws NamingException {
|
||||
|
||||
PooledConnection getAvailableConnection(long timeout) throws NamingException {
|
||||
if (!initialized) {
|
||||
PooledConnection conn = createConnection(factory, timeout);
|
||||
if (conns.size() >= initSize) {
|
||||
this.initialized = true;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
int size = conns.size(); // Current number of idle/nonidle conns
|
||||
PooledConnection conn = null;
|
||||
|
||||
if (prefSize <= 0 || size >= prefSize) {
|
||||
// If no prefSize specified, or list size already meets or
|
||||
// exceeds prefSize, then first look for an idle connection
|
||||
ConnectionDesc entry;
|
||||
for (int i = 0; i < size; i++) {
|
||||
entry = conns.get(i);
|
||||
for (ConnectionDesc connectionDesc : conns) {
|
||||
PooledConnection conn;
|
||||
entry = connectionDesc;
|
||||
if ((conn = entry.tryUse()) != null) {
|
||||
d("get(): use ", conn);
|
||||
td("Use ", conn);
|
||||
@@ -193,17 +170,25 @@ final class Connections implements PoolCallback {
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if list size already at maxSize specified
|
||||
if (maxSize > 0 && size >= maxSize) {
|
||||
return null; // List size is at limit; cannot create any more
|
||||
/*
|
||||
* Creates a new Connection if maxSize hasn't been reached.
|
||||
* If maxSize has been reached, return null.
|
||||
* Caller must hold the ReentrantLock.
|
||||
*/
|
||||
PooledConnection createConnection(PooledConnectionFactory factory, long timeout)
|
||||
throws NamingException {
|
||||
int size = conns.size(); // Current number of idle/non-idle connections
|
||||
if (maxSize == 0 || size < maxSize) {
|
||||
PooledConnection conn = factory.createPooledConnection(this, timeout);
|
||||
td("Create and use ", conn, factory);
|
||||
conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool
|
||||
return conn;
|
||||
}
|
||||
|
||||
conn = factory.createPooledConnection(this);
|
||||
td("Create and use ", conn, factory);
|
||||
conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool
|
||||
|
||||
return conn;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,43 +196,45 @@ final class Connections implements PoolCallback {
|
||||
* If the list size is below prefSize, the connection may be reused.
|
||||
* If the list size exceeds prefSize, then the connection is closed
|
||||
* and removed from the list.
|
||||
*
|
||||
* <p>
|
||||
* public because implemented as part of PoolCallback.
|
||||
*/
|
||||
public synchronized boolean releasePooledConnection(PooledConnection conn) {
|
||||
ConnectionDesc entry;
|
||||
int loc = conns.indexOf(entry=new ConnectionDesc(conn));
|
||||
public boolean releasePooledConnection(PooledConnection conn) {
|
||||
lock.lock();
|
||||
try {
|
||||
ConnectionDesc entry;
|
||||
int loc = conns.indexOf(entry = new ConnectionDesc(conn));
|
||||
|
||||
d("release(): ", conn);
|
||||
d("release(): ", conn);
|
||||
|
||||
if (loc >= 0) {
|
||||
// Found entry
|
||||
if (loc >= 0) {
|
||||
// Found entry
|
||||
|
||||
if (closed || (prefSize > 0 && conns.size() > prefSize)) {
|
||||
// If list size exceeds prefSize, close connection
|
||||
if (closed || (prefSize > 0 && conns.size() > prefSize)) {
|
||||
// If list size exceeds prefSize, close connection
|
||||
|
||||
d("release(): closing ", conn);
|
||||
td("Close ", conn);
|
||||
d("release(): closing ", conn);
|
||||
td("Close ", conn);
|
||||
|
||||
// size must be >= 2 so don't worry about empty list
|
||||
conns.remove(entry);
|
||||
conn.closeConnection();
|
||||
|
||||
} else {
|
||||
d("release(): release ", conn);
|
||||
td("Release ", conn);
|
||||
|
||||
// Get ConnectionDesc from list to get correct state info
|
||||
entry = conns.get(loc);
|
||||
// Return connection to list, ready for reuse
|
||||
entry.release();
|
||||
// size must be >= 2 so don't worry about empty list
|
||||
conns.remove(entry);
|
||||
conn.closeConnection();
|
||||
} else {
|
||||
d("release(): release ", conn);
|
||||
td("Release ", conn);
|
||||
// Get ConnectionDesc from list to get correct state info
|
||||
entry = conns.get(loc);
|
||||
// Return connection to list, ready for reuse
|
||||
entry.release();
|
||||
}
|
||||
connectionsAvailable.signalAll();
|
||||
d("release(): notify");
|
||||
return true;
|
||||
}
|
||||
notifyAll();
|
||||
d("release(): notify");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,29 +244,34 @@ final class Connections implements PoolCallback {
|
||||
* when using the connection and wants it removed from the pool.
|
||||
*
|
||||
* @return true if conn removed; false if it was not in pool
|
||||
*
|
||||
* <p>
|
||||
* public because implemented as part of PoolCallback.
|
||||
*/
|
||||
public synchronized boolean removePooledConnection(PooledConnection conn) {
|
||||
if (conns.remove(new ConnectionDesc(conn))) {
|
||||
d("remove(): ", conn);
|
||||
public boolean removePooledConnection(PooledConnection conn) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (conns.remove(new ConnectionDesc(conn))) {
|
||||
d("remove(): ", conn);
|
||||
|
||||
notifyAll();
|
||||
connectionsAvailable.signalAll();
|
||||
|
||||
d("remove(): notify");
|
||||
td("Remove ", conn);
|
||||
d("remove(): notify");
|
||||
td("Remove ", conn);
|
||||
|
||||
if (conns.isEmpty()) {
|
||||
// Remove softref to make pool entry eligible for GC.
|
||||
// Once ref has been removed, it cannot be reinstated.
|
||||
ref = null;
|
||||
if (conns.isEmpty()) {
|
||||
// Remove softref to make pool entry eligible for GC.
|
||||
// Once ref has been removed, it cannot be reinstated.
|
||||
ref = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
d("remove(): not found ", conn);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
d("remove(): not found ", conn);
|
||||
return false;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,8 +283,11 @@ final class Connections implements PoolCallback {
|
||||
*/
|
||||
boolean expire(long threshold) {
|
||||
List<ConnectionDesc> clonedConns;
|
||||
synchronized(this) {
|
||||
lock.lock();
|
||||
try {
|
||||
clonedConns = new ArrayList<>(conns);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
List<ConnectionDesc> expired = new ArrayList<>();
|
||||
|
||||
@@ -304,12 +299,15 @@ final class Connections implements PoolCallback {
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
lock.lock();
|
||||
try {
|
||||
conns.removeAll(expired);
|
||||
// Don't need to call notify() because we're
|
||||
// removing only idle connections. If there were
|
||||
// idle connections, then there should be no waiters.
|
||||
return conns.isEmpty(); // whether whole list has 'expired'
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,6 +353,29 @@ final class Connections implements PoolCallback {
|
||||
+ "; idle=" + idle + "; expired=" + expired;
|
||||
}
|
||||
|
||||
boolean grabLock(long timeout) throws InterruptedNamingException {
|
||||
final long start = System.nanoTime();
|
||||
long current = start;
|
||||
long remaining = timeout;
|
||||
boolean locked = false;
|
||||
while (!locked && remaining > 0) {
|
||||
try {
|
||||
locked = lock.tryLock(remaining, TimeUnit.MILLISECONDS);
|
||||
remaining -= TimeUnit.NANOSECONDS.toMillis(current - start);
|
||||
} catch (InterruptedException ignore) {
|
||||
throw new InterruptedNamingException(
|
||||
"Interrupted while waiting for the connection pool lock");
|
||||
}
|
||||
current = System.nanoTime();
|
||||
remaining -= TimeUnit.NANOSECONDS.toMillis(current - start);
|
||||
}
|
||||
return locked;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private void d(String msg, Object o1) {
|
||||
if (debug) {
|
||||
d(msg + o1);
|
||||
|
||||
@@ -31,10 +31,13 @@ import java.util.WeakHashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import javax.naming.CommunicationException;
|
||||
import javax.naming.NamingException;
|
||||
|
||||
/**
|
||||
@@ -117,45 +120,109 @@ public final class Pool {
|
||||
public PooledConnection getPooledConnection(Object id, long timeout,
|
||||
PooledConnectionFactory factory) throws NamingException {
|
||||
|
||||
final long start = System.nanoTime();
|
||||
long remaining = timeout;
|
||||
|
||||
d("get(): ", id);
|
||||
if (debug) {
|
||||
synchronized (map) {
|
||||
d("size: ", map.size());
|
||||
}
|
||||
remaining = checkRemaining(start, remaining);
|
||||
}
|
||||
|
||||
expungeStaleConnections();
|
||||
|
||||
Connections conns;
|
||||
synchronized (map) {
|
||||
conns = getConnections(id);
|
||||
if (conns == null) {
|
||||
d("get(): creating new connections list for ", id);
|
||||
Connections conns = getOrCreateConnections(factory, id);
|
||||
d("get(): size after: ", map.size());
|
||||
remaining = checkRemaining(start, remaining);
|
||||
|
||||
// No connections for this id so create a new list
|
||||
conns = new Connections(id, initSize, prefSize, maxSize,
|
||||
factory);
|
||||
ConnectionsRef connsRef = new ConnectionsRef(conns);
|
||||
map.put(id, connsRef);
|
||||
|
||||
// Create a weak reference to ConnectionsRef
|
||||
Reference<ConnectionsRef> weakRef =
|
||||
new ConnectionsWeakRef(connsRef, queue);
|
||||
|
||||
// Keep the weak reference through the element of a linked list
|
||||
weakRefs.add(weakRef);
|
||||
}
|
||||
d("get(): size after: ", map.size());
|
||||
if (!conns.grabLock(remaining)) {
|
||||
throw new CommunicationException("Timed out waiting for lock");
|
||||
}
|
||||
|
||||
return conns.get(timeout, factory); // get one connection from list
|
||||
try {
|
||||
remaining = checkRemaining(start, remaining);
|
||||
PooledConnection conn = null;
|
||||
while (remaining > 0 && conn == null) {
|
||||
conn = getOrCreatePooledConnection(factory, conns, start, remaining);
|
||||
// don't loop if the timeout has expired
|
||||
remaining = checkRemaining(start, timeout);
|
||||
}
|
||||
return conn;
|
||||
} finally {
|
||||
conns.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Connections getConnections(Object id) {
|
||||
ConnectionsRef ref = map.get(id);
|
||||
return (ref != null) ? ref.getConnections() : null;
|
||||
private Connections getOrCreateConnections(PooledConnectionFactory factory, Object id)
|
||||
throws NamingException {
|
||||
|
||||
Connections conns;
|
||||
synchronized (map) {
|
||||
ConnectionsRef ref = map.get(id);
|
||||
if (ref != null) {
|
||||
return ref.getConnections();
|
||||
}
|
||||
|
||||
d("get(): creating new connections list for ", id);
|
||||
|
||||
// No connections for this id so create a new list
|
||||
conns = new Connections(id, initSize, prefSize, maxSize,
|
||||
factory, new ReentrantLock());
|
||||
|
||||
ConnectionsRef connsRef = new ConnectionsRef(conns);
|
||||
map.put(id, connsRef);
|
||||
|
||||
// Create a weak reference to ConnectionsRef
|
||||
Reference<ConnectionsRef> weakRef = new ConnectionsWeakRef(connsRef, queue);
|
||||
|
||||
// Keep the weak reference through the element of a linked list
|
||||
weakRefs.add(weakRef);
|
||||
}
|
||||
return conns;
|
||||
}
|
||||
|
||||
private PooledConnection getOrCreatePooledConnection(
|
||||
PooledConnectionFactory factory, Connections conns, long start, long timeout)
|
||||
throws NamingException {
|
||||
PooledConnection conn = conns.getAvailableConnection(timeout);
|
||||
if (conn != null) {
|
||||
return conn;
|
||||
}
|
||||
// no available cached connection
|
||||
// check if list size already at maxSize before creating a new one
|
||||
conn = conns.createConnection(factory, timeout);
|
||||
if (conn != null) {
|
||||
return conn;
|
||||
}
|
||||
// max number of connections already created,
|
||||
// try waiting around for one to become available
|
||||
if (timeout <= 0) {
|
||||
conns.waitForAvailableConnection();
|
||||
} else {
|
||||
long remaining = checkRemaining(start, timeout);
|
||||
conns.waitForAvailableConnection(remaining);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check whether we timed out
|
||||
private long checkRemaining(long start, long timeout) throws CommunicationException {
|
||||
if (timeout > 0) {
|
||||
long current = System.nanoTime();
|
||||
long remaining = timeout - TimeUnit.NANOSECONDS.toMillis(current - start);
|
||||
if (remaining <= 0) {
|
||||
throw new CommunicationException(
|
||||
"Timeout exceeded while waiting for a connection: " +
|
||||
timeout + "ms");
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Goes through the connections in this Pool and expires ones that
|
||||
* have been idle before 'threshold'. An expired connection is closed
|
||||
|
||||
@@ -44,4 +44,13 @@ public interface PooledConnectionFactory {
|
||||
*/
|
||||
public abstract PooledConnection createPooledConnection(PoolCallback pcb)
|
||||
throws NamingException;
|
||||
|
||||
/**
|
||||
* Creates a pooled connection.
|
||||
* @param pcb callback responsible for removing and releasing the pooled
|
||||
* connection from the pool.
|
||||
* @param timeout the connection timeout
|
||||
*/
|
||||
public abstract PooledConnection createPooledConnection(PoolCallback pcb, long timeout)
|
||||
throws NamingException;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@@ -32,6 +32,8 @@ import javax.naming.spi.NamingManager;
|
||||
import java.util.Hashtable;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import com.sun.jndi.toolkit.url.Uri.ParseMode;
|
||||
|
||||
/**
|
||||
* This abstract class is a generic URL context that accepts as the
|
||||
* name argument either a string URL or a Name whose first component
|
||||
@@ -48,6 +50,7 @@ import java.net.MalformedURLException;
|
||||
* @author Rosanna Lee
|
||||
*/
|
||||
public abstract class GenericURLContext implements Context {
|
||||
|
||||
protected Hashtable<String, Object> myEnv = null;
|
||||
|
||||
@SuppressWarnings("unchecked") // Expect Hashtable<String, Object>
|
||||
@@ -161,8 +164,18 @@ public abstract class GenericURLContext implements Context {
|
||||
if (url.startsWith("//", start)) {
|
||||
start += 2; // skip double slash
|
||||
|
||||
// find last slash
|
||||
int posn = url.indexOf('/', start);
|
||||
// find where the authority component ends
|
||||
// and the rest of the URL starts
|
||||
int slash = url.indexOf('/', start);
|
||||
int qmark = url.indexOf('?', start);
|
||||
int fmark = url.indexOf('#', start);
|
||||
if (fmark > -1 && qmark > fmark) qmark = -1;
|
||||
if (fmark > -1 && slash > fmark) slash = -1;
|
||||
if (qmark > -1 && slash > qmark) slash = -1;
|
||||
int posn = slash > -1 ? slash
|
||||
: (qmark > -1 ? qmark
|
||||
: (fmark > -1 ? fmark
|
||||
: url.length()));
|
||||
if (posn >= 0) {
|
||||
start = posn;
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -27,6 +27,8 @@ package com.sun.jndi.toolkit.url;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,15 +38,17 @@ import java.net.MalformedURLException;
|
||||
*
|
||||
* <p> The java.net.URL class cannot be used to parse URIs since it
|
||||
* requires the installation of URL stream handlers that may not be
|
||||
* available. The hack of getting around this by temporarily
|
||||
* replacing the scheme part of a URI is not appropriate here: JNDI
|
||||
* service providers must work on older Java platforms, and we want
|
||||
* new features and bug fixes that are not available in old versions
|
||||
* of the URL class.
|
||||
* available.
|
||||
*
|
||||
* <p> It may be appropriate to drop this code in favor of the
|
||||
* java.net.URI class. The changes would need to be written so as to
|
||||
* still run on pre-1.4 platforms not containing that class.
|
||||
* <p> The {@linkplain ParseMode#STRICT strict} parsing mode uses
|
||||
* the java.net.URI class to syntactically validate URI strings.
|
||||
* The {@linkplain ParseMode#COMPAT compat} mode validate the
|
||||
* URI authority and rejects URI fragments, but doesn't perform any
|
||||
* additional validation on path and query, other than that
|
||||
* which may be implemented in the concrete the Uri subclasses.
|
||||
* The {@linkplain ParseMode#LEGACY legacy} mode should not be
|
||||
* used unless the application is capable of validating all URI
|
||||
* strings before any constructors of this class is invoked.
|
||||
*
|
||||
* <p> The format of an absolute URI (see the RFCs mentioned above) is:
|
||||
* <blockquote><pre>{@code
|
||||
@@ -105,6 +109,28 @@ import java.net.MalformedURLException;
|
||||
|
||||
public class Uri {
|
||||
|
||||
// three parsing modes
|
||||
public enum ParseMode {
|
||||
/**
|
||||
* Strict validation mode.
|
||||
* Validate the URI syntactically using {@link java.net.URI}.
|
||||
* Rejects URI fragments unless explicitly supported by the
|
||||
* subclass.
|
||||
*/
|
||||
STRICT,
|
||||
/**
|
||||
* Compatibility mode. The URI authority is syntactically validated.
|
||||
* Rejects URI fragments unless explicitly supported by the
|
||||
* subclass.
|
||||
* This is the default.
|
||||
*/
|
||||
COMPAT,
|
||||
/**
|
||||
* Legacy mode. In this mode, no validation is performed.
|
||||
*/
|
||||
LEGACY
|
||||
}
|
||||
|
||||
protected String uri;
|
||||
protected String scheme;
|
||||
protected String host = null;
|
||||
@@ -112,6 +138,7 @@ public class Uri {
|
||||
protected boolean hasAuthority;
|
||||
protected String path;
|
||||
protected String query = null;
|
||||
protected String fragment;
|
||||
|
||||
|
||||
/**
|
||||
@@ -128,6 +155,15 @@ public class Uri {
|
||||
protected Uri() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The parse mode for parsing this URI.
|
||||
* The default is {@link ParseMode#COMPAT}.
|
||||
* @return the parse mode for parsing this URI.
|
||||
*/
|
||||
protected ParseMode parseMode() {
|
||||
return ParseMode.COMPAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a Uri object given a URI string.
|
||||
* This method must be called exactly once, and before any other Uri
|
||||
@@ -135,7 +171,7 @@ public class Uri {
|
||||
*/
|
||||
protected void init(String uri) throws MalformedURLException {
|
||||
this.uri = uri;
|
||||
parse(uri);
|
||||
parse(uri, parseMode());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,10 +224,229 @@ public class Uri {
|
||||
return uri;
|
||||
}
|
||||
|
||||
private void parse(String uri, ParseMode mode) throws MalformedURLException {
|
||||
switch (mode) {
|
||||
case STRICT -> parseStrict(uri);
|
||||
case COMPAT -> parseCompat(uri);
|
||||
case LEGACY -> parseLegacy(uri);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a URI string and sets this object's fields accordingly.
|
||||
* Use java.net.URI to validate the uri string syntax
|
||||
*/
|
||||
private void parse(String uri) throws MalformedURLException {
|
||||
private void parseStrict(String uri) throws MalformedURLException {
|
||||
try {
|
||||
if (!isSchemeOnly(uri)) {
|
||||
URI u = new URI(uri);
|
||||
scheme = u.getScheme();
|
||||
if (scheme == null) throw new MalformedURLException("Invalid URI: " + uri);
|
||||
var auth = u.getRawAuthority();
|
||||
hasAuthority = auth != null;
|
||||
if (hasAuthority) {
|
||||
var host = u.getHost();
|
||||
var port = u.getPort();
|
||||
if (host != null) this.host = host;
|
||||
if (port != -1) this.port = port;
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1 ? "" : (":" + port));
|
||||
if (!hostport.equals(auth)) {
|
||||
// throw if we have user info or regname
|
||||
throw new MalformedURLException("unsupported authority: " + auth);
|
||||
}
|
||||
}
|
||||
path = u.getRawPath();
|
||||
if (u.getRawQuery() != null) {
|
||||
query = "?" + u.getRawQuery();
|
||||
}
|
||||
if (u.getRawFragment() != null) {
|
||||
if (!acceptsFragment()) {
|
||||
throw new MalformedURLException("URI fragments not supported: " + uri);
|
||||
}
|
||||
fragment = "#" + u.getRawFragment();
|
||||
}
|
||||
} else {
|
||||
// scheme-only URIs are not supported by java.net.URI
|
||||
// validate the URI by appending "/" to the uri string.
|
||||
var s = uri.substring(0, uri.indexOf(':'));
|
||||
URI u = new URI(uri + "/");
|
||||
if (!s.equals(u.getScheme())
|
||||
|| !checkSchemeOnly(uri, u.getScheme())) {
|
||||
throw newInvalidURISchemeException(uri);
|
||||
}
|
||||
scheme = s;
|
||||
path = "";
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
var mue = new MalformedURLException(e.getMessage());
|
||||
mue.initCause(e);
|
||||
throw mue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parses a URI string and sets this object's fields accordingly.
|
||||
* Compatibility mode. Use java.net.URI to validate the syntax of
|
||||
* the uri string authority.
|
||||
*/
|
||||
private void parseCompat(String uri) throws MalformedURLException {
|
||||
int i; // index into URI
|
||||
|
||||
i = uri.indexOf(':'); // parse scheme
|
||||
int slash = uri.indexOf('/');
|
||||
int qmark = uri.indexOf('?');
|
||||
int fmark = uri.indexOf('#');
|
||||
if (i < 0 || slash > 0 && i > slash || qmark > 0 && i > qmark || fmark > 0 && i > fmark) {
|
||||
throw new MalformedURLException("Invalid URI: " + uri);
|
||||
}
|
||||
if (fmark > -1) {
|
||||
if (!acceptsFragment()) {
|
||||
throw new MalformedURLException("URI fragments not supported: " + uri);
|
||||
}
|
||||
}
|
||||
if (i == uri.length() - 1) {
|
||||
if (!isSchemeOnly(uri)) {
|
||||
throw newInvalidURISchemeException(uri);
|
||||
}
|
||||
}
|
||||
scheme = uri.substring(0, i);
|
||||
i++; // skip past ":"
|
||||
|
||||
hasAuthority = uri.startsWith("//", i);
|
||||
if (fmark > -1 && qmark > fmark) qmark = -1;
|
||||
int endp = qmark > -1 ? qmark : fmark > -1 ? fmark : uri.length();
|
||||
if (hasAuthority) { // parse "//host:port"
|
||||
i += 2; // skip past "//"
|
||||
int starta = i;
|
||||
// authority ends at the first appearance of /, ?, or #
|
||||
int enda = uri.indexOf('/', i);
|
||||
if (enda == -1 || qmark > -1 && qmark < enda) enda = qmark;
|
||||
if (enda == -1 || fmark > -1 && fmark < enda) enda = fmark;
|
||||
if (enda < 0) {
|
||||
enda = uri.length();
|
||||
}
|
||||
if (uri.startsWith(":", i)) {
|
||||
// LdapURL supports empty host.
|
||||
i++;
|
||||
host = "";
|
||||
if (enda > i) {
|
||||
port = Integer.parseInt(uri.substring(i, enda));
|
||||
}
|
||||
} else {
|
||||
// Use URI to parse authority
|
||||
try {
|
||||
// URI requires at least one char after authority:
|
||||
// we use "/" and expect that the resulting URI path
|
||||
// will be exactly "/".
|
||||
URI u = new URI(uri.substring(0, enda) + "/");
|
||||
String auth = uri.substring(starta, enda);
|
||||
host = u.getHost();
|
||||
port = u.getPort();
|
||||
String p = u.getRawPath();
|
||||
String q = u.getRawQuery();
|
||||
String f = u.getRawFragment();
|
||||
String ui = u.getRawUserInfo();
|
||||
if (ui != null) {
|
||||
throw new MalformedURLException("user info not supported in authority: " + ui);
|
||||
}
|
||||
if (!"/".equals(p)) {
|
||||
throw new MalformedURLException("invalid authority: " + auth);
|
||||
}
|
||||
if (q != null) {
|
||||
throw new MalformedURLException("invalid trailing characters in authority: ?" + q);
|
||||
}
|
||||
if (f != null) {
|
||||
throw new MalformedURLException("invalid trailing characters in authority: #" + f);
|
||||
}
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1?"":(":" + port));
|
||||
if (!auth.equals(hostport)) {
|
||||
// throw if we have user info or regname
|
||||
throw new MalformedURLException("unsupported authority: " + auth);
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
var mue = new MalformedURLException(e.getMessage());
|
||||
mue.initCause(e);
|
||||
throw mue;
|
||||
}
|
||||
}
|
||||
i = enda;
|
||||
}
|
||||
path = uri.substring(i, endp);
|
||||
// look for query
|
||||
if (qmark > -1) {
|
||||
if (fmark > -1) {
|
||||
query = uri.substring(qmark, fmark);
|
||||
} else {
|
||||
query = uri.substring(qmark);
|
||||
}
|
||||
}
|
||||
if (fmark > -1) {
|
||||
fragment = uri.substring(fmark);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass of {@code Uri} that supports scheme only
|
||||
* URIs can override this method and return true in the
|
||||
* case where the URI string is a scheme-only URI that
|
||||
* the subclass supports.
|
||||
* @implSpec
|
||||
* The default implementation of this method returns false,
|
||||
* always.
|
||||
* @param uri An URI string
|
||||
* @return if this is a scheme-only URI supported by the subclass
|
||||
*/
|
||||
protected boolean isSchemeOnly(String uri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given uri string should be considered
|
||||
* as a scheme-only URI. For some protocols - e.g. DNS, we
|
||||
* might accept "dns://" as a valid URL denoting default DNS.
|
||||
* For others - we might only accept "scheme:".
|
||||
* @implSpec
|
||||
* The default implementation of this method returns true if
|
||||
* the URI is of the form {@code "<scheme>:"} with nothing
|
||||
* after the scheme delimiter.
|
||||
* @param uri the URI
|
||||
* @param scheme the scheme
|
||||
* @return true if the URI should be considered as a scheme-only
|
||||
* URI supported by this URI scheme.
|
||||
*/
|
||||
protected boolean checkSchemeOnly(String uri, String scheme) {
|
||||
return uri.equals(scheme + ":");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code MalformedURLException} to be thrown when the
|
||||
* URI scheme is not supported.
|
||||
*
|
||||
* @param uri the URI string
|
||||
* @return a {@link MalformedURLException}
|
||||
*/
|
||||
protected MalformedURLException newInvalidURISchemeException(String uri) {
|
||||
return new MalformedURLException("Invalid URI scheme: " + uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether fragments are supported.
|
||||
* @implSpec
|
||||
* The default implementation of this method retturns false, always.
|
||||
* @return true if fragments are supported.
|
||||
*/
|
||||
protected boolean acceptsFragment() {
|
||||
return parseMode() == ParseMode.LEGACY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a URI string and sets this object's fields accordingly.
|
||||
* Legacy parsing mode.
|
||||
*/
|
||||
private void parseLegacy(String uri) throws MalformedURLException {
|
||||
int i; // index into URI
|
||||
|
||||
i = uri.indexOf(':'); // parse scheme
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@@ -56,6 +56,7 @@ import com.sun.org.apache.xerces.internal.xs.ElementPSVI;
|
||||
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
|
||||
import java.util.Locale;
|
||||
import java.util.Stack;
|
||||
import jdk.xml.internal.JdkXmlUtils;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.CDATASection;
|
||||
import org.w3c.dom.Comment;
|
||||
@@ -84,7 +85,7 @@ import org.xml.sax.SAXException;
|
||||
* @author Andy Clark, IBM
|
||||
* @author Elena Litani, IBM
|
||||
*
|
||||
* @LastModified: Jan 2019
|
||||
* @LastModified: July 2021
|
||||
*/
|
||||
public class AbstractDOMParser extends AbstractXMLDocumentParser {
|
||||
|
||||
@@ -2041,17 +2042,8 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
|
||||
else {
|
||||
fInternalSubset.append (name);
|
||||
}
|
||||
fInternalSubset.append (' ');
|
||||
if (publicId != null) {
|
||||
fInternalSubset.append ("PUBLIC '");
|
||||
fInternalSubset.append (publicId);
|
||||
fInternalSubset.append ("' '");
|
||||
}
|
||||
else {
|
||||
fInternalSubset.append ("SYSTEM '");
|
||||
}
|
||||
fInternalSubset.append (literalSystemId);
|
||||
fInternalSubset.append ("'>\n");
|
||||
fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId));
|
||||
fInternalSubset.append (">\n");
|
||||
}
|
||||
|
||||
// NOTE: We only know how to create these nodes for the Xerces
|
||||
@@ -2181,20 +2173,8 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
|
||||
if (fInternalSubset != null && !fInDTDExternalSubset) {
|
||||
fInternalSubset.append ("<!ENTITY ");
|
||||
fInternalSubset.append (name);
|
||||
fInternalSubset.append (' ');
|
||||
if (publicId != null) {
|
||||
fInternalSubset.append ("PUBLIC '");
|
||||
fInternalSubset.append (publicId);
|
||||
if (literalSystemId != null) {
|
||||
fInternalSubset.append ("' '");
|
||||
fInternalSubset.append (literalSystemId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fInternalSubset.append ("SYSTEM '");
|
||||
fInternalSubset.append (literalSystemId);
|
||||
}
|
||||
fInternalSubset.append ("' NDATA ");
|
||||
fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId));
|
||||
fInternalSubset.append (" NDATA ");
|
||||
fInternalSubset.append (notation);
|
||||
fInternalSubset.append (">\n");
|
||||
}
|
||||
@@ -2261,19 +2241,8 @@ public class AbstractDOMParser extends AbstractXMLDocumentParser {
|
||||
if (fInternalSubset != null && !fInDTDExternalSubset) {
|
||||
fInternalSubset.append ("<!NOTATION ");
|
||||
fInternalSubset.append (name);
|
||||
if (publicId != null) {
|
||||
fInternalSubset.append (" PUBLIC '");
|
||||
fInternalSubset.append (publicId);
|
||||
if (literalSystemId != null) {
|
||||
fInternalSubset.append ("' '");
|
||||
fInternalSubset.append (literalSystemId);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fInternalSubset.append (" SYSTEM '");
|
||||
fInternalSubset.append (literalSystemId);
|
||||
}
|
||||
fInternalSubset.append ("'>\n");
|
||||
fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId));
|
||||
fInternalSubset.append (">\n");
|
||||
}
|
||||
|
||||
// NOTE: We only know how to create these nodes for the Xerces
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.xml.sax.SAXException;
|
||||
import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
|
||||
import com.sun.org.apache.xml.internal.serializer.utils.Utils;
|
||||
import javax.xml.transform.ErrorListener;
|
||||
import jdk.xml.internal.JdkXmlUtils;
|
||||
|
||||
/**
|
||||
* This serializer takes a series of SAX or
|
||||
@@ -41,7 +42,7 @@ import javax.xml.transform.ErrorListener;
|
||||
* because it is used from another package.
|
||||
*
|
||||
* @xsl.usage internal
|
||||
* @LastModified: June 2021
|
||||
* @LastModified: July 2021
|
||||
*/
|
||||
public final class ToHTMLStream extends ToStream
|
||||
{
|
||||
@@ -679,28 +680,10 @@ public final class ToHTMLStream extends ToStream
|
||||
final java.io.Writer writer = m_writer;
|
||||
try
|
||||
{
|
||||
writer.write("<!DOCTYPE html");
|
||||
|
||||
if (null != doctypePublic)
|
||||
{
|
||||
writer.write(" PUBLIC \"");
|
||||
writer.write(doctypePublic);
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
if (null != doctypeSystem)
|
||||
{
|
||||
if (null == doctypePublic)
|
||||
writer.write(" SYSTEM \"");
|
||||
else
|
||||
writer.write(" \"");
|
||||
|
||||
writer.write(doctypeSystem);
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
writer.write('>');
|
||||
outputLineSep();
|
||||
writer.write("<!DOCTYPE html");
|
||||
writer.write(JdkXmlUtils.getDTDExternalDecl(doctypePublic, doctypeSystem));
|
||||
writer.write('>');
|
||||
outputLineSep();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
||||
@@ -54,7 +54,7 @@ import org.xml.sax.SAXException;
|
||||
* serializers (xml, html, text ...) that write output to a stream.
|
||||
*
|
||||
* @xsl.usage internal
|
||||
* @LastModified: June 2021
|
||||
* @LastModified: July 2021
|
||||
*/
|
||||
abstract public class ToStream extends SerializerBase {
|
||||
|
||||
@@ -895,16 +895,8 @@ abstract public class ToStream extends SerializerBase {
|
||||
|
||||
m_writer.write("<!ENTITY ");
|
||||
m_writer.write(name);
|
||||
if (publicId != null) {
|
||||
m_writer.write(" PUBLIC \"");
|
||||
m_writer.write(publicId);
|
||||
|
||||
}
|
||||
else {
|
||||
m_writer.write(" SYSTEM \"");
|
||||
m_writer.write(systemId);
|
||||
}
|
||||
m_writer.write("\" >");
|
||||
m_writer.write(JdkXmlUtils.getDTDExternalDecl(publicId, systemId));
|
||||
m_writer.write(">");
|
||||
m_writer.write(m_lineSep, 0, m_lineSepLen);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
@@ -1967,27 +1959,11 @@ abstract public class ToStream extends SerializerBase {
|
||||
final Writer writer = m_writer;
|
||||
writer.write("<!DOCTYPE ");
|
||||
writer.write(name);
|
||||
String systemId = getDoctypeSystem();
|
||||
writer.write(JdkXmlUtils.getDTDExternalDecl(getDoctypePublic(), systemId));
|
||||
|
||||
String doctypePublic = getDoctypePublic();
|
||||
if (null != doctypePublic)
|
||||
if (null != systemId)
|
||||
{
|
||||
writer.write(" PUBLIC \"");
|
||||
writer.write(doctypePublic);
|
||||
writer.write('\"');
|
||||
}
|
||||
|
||||
String doctypeSystem = getDoctypeSystem();
|
||||
if (null != doctypeSystem)
|
||||
{
|
||||
char quote = JdkXmlUtils.getQuoteChar(doctypeSystem);
|
||||
if (null == doctypePublic) {
|
||||
writer.write(" SYSTEM");
|
||||
}
|
||||
writer.write(" ");
|
||||
writer.write(quote);
|
||||
|
||||
writer.write(doctypeSystem);
|
||||
writer.write(quote);
|
||||
if (closeDecl)
|
||||
{
|
||||
writer.write(">");
|
||||
@@ -1995,17 +1971,6 @@ abstract public class ToStream extends SerializerBase {
|
||||
closeDecl = false; // done closing
|
||||
}
|
||||
}
|
||||
boolean dothis = false;
|
||||
if (dothis)
|
||||
{
|
||||
// at one point this code seemed right,
|
||||
// but not anymore - Brian M.
|
||||
if (closeDecl)
|
||||
{
|
||||
writer.write('>');
|
||||
writer.write(m_lineSep, 0, m_lineSepLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
@@ -3570,16 +3535,8 @@ abstract public class ToStream extends SerializerBase {
|
||||
|
||||
m_writer.write("<!NOTATION ");
|
||||
m_writer.write(name);
|
||||
if (pubID != null) {
|
||||
m_writer.write(" PUBLIC \"");
|
||||
m_writer.write(pubID);
|
||||
|
||||
}
|
||||
else {
|
||||
m_writer.write(" SYSTEM \"");
|
||||
m_writer.write(sysID);
|
||||
}
|
||||
m_writer.write("\" >");
|
||||
m_writer.write(JdkXmlUtils.getDTDExternalDecl(pubID, sysID));
|
||||
m_writer.write(">");
|
||||
m_writer.write(m_lineSep, 0, m_lineSepLen);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
@@ -3600,16 +3557,8 @@ abstract public class ToStream extends SerializerBase {
|
||||
|
||||
m_writer.write("<!ENTITY ");
|
||||
m_writer.write(name);
|
||||
if (pubID != null) {
|
||||
m_writer.write(" PUBLIC \"");
|
||||
m_writer.write(pubID);
|
||||
|
||||
}
|
||||
else {
|
||||
m_writer.write(" SYSTEM \"");
|
||||
m_writer.write(sysID);
|
||||
}
|
||||
m_writer.write("\" NDATA ");
|
||||
m_writer.write(JdkXmlUtils.getDTDExternalDecl(pubID, sysID));
|
||||
m_writer.write(" NDATA ");
|
||||
m_writer.write(notationName);
|
||||
m_writer.write(" >");
|
||||
m_writer.write(m_lineSep, 0, m_lineSepLen);
|
||||
|
||||
@@ -63,7 +63,7 @@ import org.xml.sax.helpers.LocatorImpl;
|
||||
* parameters and filters if any during serialization.
|
||||
*
|
||||
* @xsl.usage internal
|
||||
* @LastModified: Apr 2021
|
||||
* @LastModified: July 2021
|
||||
*/
|
||||
final class DOM3TreeWalker {
|
||||
|
||||
@@ -506,23 +506,7 @@ final class DOM3TreeWalker {
|
||||
|
||||
dtd.append("<!DOCTYPE ");
|
||||
dtd.append(docTypeName);
|
||||
if (null != publicId) {
|
||||
dtd.append(" PUBLIC \"");
|
||||
dtd.append(publicId);
|
||||
dtd.append('\"');
|
||||
}
|
||||
|
||||
if (null != systemId) {
|
||||
char quote = JdkXmlUtils.getQuoteChar(systemId);
|
||||
if (null == publicId) {
|
||||
dtd.append(" SYSTEM ").append(quote);
|
||||
} else {
|
||||
dtd.append(" ").append(quote);
|
||||
}
|
||||
dtd.append(systemId);
|
||||
dtd.append(quote);
|
||||
}
|
||||
|
||||
dtd.append(JdkXmlUtils.getDTDExternalDecl(publicId, systemId));
|
||||
dtd.append(" [ ");
|
||||
|
||||
dtd.append(fNewLine);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, 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
|
||||
@@ -28,6 +28,7 @@ package com.sun.xml.internal.stream.events;
|
||||
import javax.xml.stream.events.EntityDeclaration;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
|
||||
import jdk.xml.internal.JdkXmlUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -129,18 +130,12 @@ public class EntityDeclarationImpl extends DummyEvent implements EntityDeclarati
|
||||
//escape quotes, lt and amps
|
||||
writer.write(" \"");
|
||||
charEncode(writer, fReplacementText);
|
||||
writer.write("\"");
|
||||
} else {
|
||||
//external entity
|
||||
String pubId = getPublicId();
|
||||
if (pubId != null) {
|
||||
writer.write(" PUBLIC \"");
|
||||
writer.write(pubId);
|
||||
} else {
|
||||
writer.write(" SYSTEM \"");
|
||||
writer.write(getSystemId());
|
||||
}
|
||||
writer.write(JdkXmlUtils.getDTDExternalDecl(getPublicId(), getSystemId()));
|
||||
}
|
||||
writer.write("\"");
|
||||
|
||||
if (fNotationName != null) {
|
||||
writer.write(" NDATA ");
|
||||
writer.write(fNotationName);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2021, 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
|
||||
@@ -28,6 +28,7 @@ package com.sun.xml.internal.stream.events;
|
||||
import javax.xml.stream.events.NotationDeclaration;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import com.sun.xml.internal.stream.dtd.nonvalidating.XMLNotationDecl;
|
||||
import jdk.xml.internal.JdkXmlUtils;
|
||||
|
||||
/**
|
||||
* Implementation of NotationDeclaration event.
|
||||
@@ -88,16 +89,7 @@ public class NotationDeclarationImpl extends DummyEvent implements NotationDecla
|
||||
{
|
||||
writer.write("<!NOTATION ");
|
||||
writer.write(getName());
|
||||
if (fPublicId != null) {
|
||||
writer.write(" PUBLIC \"");
|
||||
writer.write(fPublicId);
|
||||
writer.write("\"");
|
||||
} else if (fSystemId != null) {
|
||||
writer.write(" SYSTEM");
|
||||
writer.write(" \"");
|
||||
writer.write(fSystemId);
|
||||
writer.write("\"");
|
||||
}
|
||||
writer.write(JdkXmlUtils.getDTDExternalDecl(fPublicId, fSystemId));
|
||||
writer.write('>');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,19 +367,40 @@ public class JdkXmlUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character to be used to quote the input content. Between
|
||||
* single and double quotes, this method returns the one that is not found
|
||||
* in the input. Returns double quote by default.
|
||||
* Returns the external declaration for a DTD construct.
|
||||
*
|
||||
* @param s the input string
|
||||
* @return returns the quote not found in the input
|
||||
* @param publicId the public identifier
|
||||
* @param systemId the system identifier
|
||||
* @return a DTD external declaration
|
||||
*/
|
||||
public static char getQuoteChar(String s) {
|
||||
if (s != null && s.indexOf('"') > -1) {
|
||||
return '\'';
|
||||
} else {
|
||||
return '"';
|
||||
public static String getDTDExternalDecl(String publicId, String systemId) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (null != publicId) {
|
||||
sb.append(" PUBLIC ");
|
||||
sb.append(quoteString(publicId));
|
||||
}
|
||||
|
||||
if (null != systemId) {
|
||||
if (null == publicId) {
|
||||
sb.append(" SYSTEM ");
|
||||
} else {
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
sb.append(quoteString(systemId));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input string quoted with double quotes or single ones if
|
||||
* there is a double quote in the string.
|
||||
* @param s the input string, can not be null
|
||||
* @return the quoted string
|
||||
*/
|
||||
private static String quoteString(String s) {
|
||||
char c = (s.indexOf('"') > -1) ? '\'' : '"';
|
||||
return c + s + c;
|
||||
}
|
||||
|
||||
private static XMLReader getXMLReaderWSAXFactory(boolean overrideDefaultParser) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2021, 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
|
||||
@@ -30,6 +30,7 @@ import sun.security.util.ArrayUtil;
|
||||
import sun.security.util.math.*;
|
||||
import static sun.security.ec.ECOperations.IntermediateValueException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.*;
|
||||
import java.util.Arrays;
|
||||
@@ -200,7 +201,8 @@ public class ECDSAOperations {
|
||||
|
||||
IntegerFieldModuloP field = ecOps.getField();
|
||||
IntegerFieldModuloP orderField = ecOps.getOrderField();
|
||||
int length = (orderField.getSize().bitLength() + 7) / 8;
|
||||
BigInteger mod = orderField.getSize();
|
||||
int length = (mod.bitLength() + 7) / 8;
|
||||
|
||||
byte[] r;
|
||||
byte[] s;
|
||||
@@ -218,6 +220,13 @@ public class ECDSAOperations {
|
||||
System.arraycopy(sig, encodeLength, s, length - encodeLength, encodeLength);
|
||||
}
|
||||
|
||||
BigInteger rb = new BigInteger(1, r);
|
||||
BigInteger sb = new BigInteger(1, s);
|
||||
if (rb.signum() == 0 || sb.signum() == 0
|
||||
|| rb.compareTo(mod) >= 0 || sb.compareTo(mod) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArrayUtil.reverse(r);
|
||||
ArrayUtil.reverse(s);
|
||||
IntegerModuloP ri = orderField.getElement(r);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2022, 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
|
||||
@@ -849,7 +849,7 @@ final class CompilerToVM {
|
||||
/**
|
||||
* @see HotSpotJVMCIRuntime#translate(Object)
|
||||
*/
|
||||
native long translate(Object obj);
|
||||
native long translate(Object obj, boolean callPostTranslation);
|
||||
|
||||
/**
|
||||
* @see HotSpotJVMCIRuntime#unhand(Class, long)
|
||||
|
||||
@@ -109,22 +109,12 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider {
|
||||
HotSpotCompiledCode hsCompiledCode = (HotSpotCompiledCode) compiledCode;
|
||||
String name = hsCompiledCode.getName();
|
||||
HotSpotCompiledNmethod hsCompiledNmethod = null;
|
||||
if (method == null) {
|
||||
// Must be a stub
|
||||
resultInstalledCode = new HotSpotRuntimeStub(name);
|
||||
} else {
|
||||
hsCompiledNmethod = (HotSpotCompiledNmethod) hsCompiledCode;
|
||||
HotSpotResolvedJavaMethodImpl hsMethod = (HotSpotResolvedJavaMethodImpl) method;
|
||||
resultInstalledCode = new HotSpotNmethod(hsMethod, name, isDefault, hsCompiledNmethod.id);
|
||||
}
|
||||
|
||||
HotSpotSpeculationLog speculationLog = null;
|
||||
if (log != null) {
|
||||
if (log.hasSpeculations()) {
|
||||
speculationLog = (HotSpotSpeculationLog) log;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] speculations;
|
||||
long failedSpeculationsAddress;
|
||||
if (speculationLog != null) {
|
||||
@@ -134,6 +124,18 @@ public class HotSpotCodeCacheProvider implements CodeCacheProvider {
|
||||
speculations = new byte[0];
|
||||
failedSpeculationsAddress = 0L;
|
||||
}
|
||||
|
||||
if (method == null) {
|
||||
// Must be a stub
|
||||
resultInstalledCode = new HotSpotRuntimeStub(name);
|
||||
} else {
|
||||
hsCompiledNmethod = (HotSpotCompiledNmethod) hsCompiledCode;
|
||||
HotSpotResolvedJavaMethodImpl hsMethod = (HotSpotResolvedJavaMethodImpl) method;
|
||||
HotSpotNmethod nmethod = new HotSpotNmethod(hsMethod, name, isDefault, hsCompiledNmethod.id);
|
||||
nmethod.setSpeculationLog(speculationLog);
|
||||
resultInstalledCode = nmethod;
|
||||
}
|
||||
|
||||
int result = runtime.getCompilerToVM().installCode(target, (HotSpotCompiledCode) compiledCode, resultInstalledCode, failedSpeculationsAddress, speculations);
|
||||
if (result != config.codeInstallResultOk) {
|
||||
String resultDesc = config.getCodeInstallResultDescription(result);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, 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
|
||||
@@ -46,6 +46,8 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.vm.ci.code.Architecture;
|
||||
@@ -66,6 +68,7 @@ import jdk.vm.ci.runtime.JVMCICompiler;
|
||||
import jdk.vm.ci.runtime.JVMCICompilerFactory;
|
||||
import jdk.vm.ci.runtime.JVMCIRuntime;
|
||||
import jdk.vm.ci.services.JVMCIServiceLocator;
|
||||
import jdk.vm.ci.services.Services;
|
||||
|
||||
/**
|
||||
* HotSpot implementation of a JVMCI runtime.
|
||||
@@ -199,14 +202,44 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the exception encoded in {@code buffer} and throws it.
|
||||
*
|
||||
* @param buffer a native byte buffer containing an exception encoded by
|
||||
* {@link #encodeThrowable}
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static Throwable decodeThrowable(String encodedThrowable) throws Throwable {
|
||||
return TranslatedException.decodeThrowable(encodedThrowable);
|
||||
static void decodeAndThrowThrowable(long buffer) throws Throwable {
|
||||
Unsafe unsafe = UnsafeAccess.UNSAFE;
|
||||
int encodingLength = unsafe.getInt(buffer);
|
||||
byte[] encoding = new byte[encodingLength];
|
||||
unsafe.copyMemory(null, buffer + 4, encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, encodingLength);
|
||||
throw TranslatedException.decodeThrowable(encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code bufferSize} is large enough, encodes {@code throwable} into a byte array and writes
|
||||
* it to {@code buffer}. The encoding in {@code buffer} can be decoded by
|
||||
* {@link #decodeAndThrowThrowable}.
|
||||
*
|
||||
* @param throwable the exception to encode
|
||||
* @param buffer a native byte buffer
|
||||
* @param bufferSize the size of {@code buffer} in bytes
|
||||
* @return the number of bytes written into {@code buffer} if {@code bufferSize} is large
|
||||
* enough, otherwise {@code -N} where {@code N} is the value {@code bufferSize} needs to
|
||||
* be to fit the encoding
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static String encodeThrowable(Throwable throwable) throws Throwable {
|
||||
return TranslatedException.encodeThrowable(throwable);
|
||||
static int encodeThrowable(Throwable throwable, long buffer, int bufferSize) throws Throwable {
|
||||
byte[] encoding = TranslatedException.encodeThrowable(throwable);
|
||||
int requiredSize = 4 + encoding.length;
|
||||
if (bufferSize < requiredSize) {
|
||||
return -requiredSize;
|
||||
}
|
||||
Unsafe unsafe = UnsafeAccess.UNSAFE;
|
||||
unsafe.putInt(buffer, encoding.length);
|
||||
unsafe.copyMemory(encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, encoding.length);
|
||||
return requiredSize;
|
||||
}
|
||||
|
||||
@VMEntryPoint
|
||||
@@ -235,6 +268,10 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
// Note: The following one is not used (see InitTimer.ENABLED). It is added here
|
||||
// so that -XX:+JVMCIPrintProperties shows the option.
|
||||
InitTimer(Boolean.class, false, "Specifies if initialization timing is enabled."),
|
||||
ForceTranslateFailure(String.class, null, "Forces HotSpotJVMCIRuntime.translate to throw an exception in the context " +
|
||||
"of the peer runtime. The value is a filter that can restrict the forced failure to matching translated " +
|
||||
"objects. See HotSpotJVMCIRuntime.postTranslation for more details. This option exists soley to test " +
|
||||
"correct handling of translation failure."),
|
||||
PrintConfig(Boolean.class, false, "Prints VM configuration available via JVMCI."),
|
||||
AuditHandles(Boolean.class, false, "Record stack trace along with scoped foreign object reference wrappers " +
|
||||
"to debug issue with a wrapper being used after its scope has closed."),
|
||||
@@ -1180,7 +1217,88 @@ public final class HotSpotJVMCIRuntime implements JVMCIRuntime {
|
||||
* @see "https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references"
|
||||
*/
|
||||
public long translate(Object obj) {
|
||||
return compilerToVm.translate(obj);
|
||||
return compilerToVm.translate(obj, Option.ForceTranslateFailure.getString() != null);
|
||||
}
|
||||
|
||||
private static final Pattern FORCE_TRANSLATE_FAILURE_FILTER_RE = Pattern.compile("(?:(method|type|nmethod)/)?([^:]+)(?::(hotspot|native))?");
|
||||
|
||||
/**
|
||||
* Forces translation failure based on {@code translatedObject} and the value of
|
||||
* {@link Option#ForceTranslateFailure}. The value is zero or more filters separated by a comma.
|
||||
* The syntax for a filter is:
|
||||
*
|
||||
* <pre>
|
||||
* Filter = [ TypeSelector "/" ] Substring [ ":" JVMCIEnvSelector ] .
|
||||
* TypeSelector = "type" | "method" | "nmethod"
|
||||
* JVMCIEnvSelector = "native" | "hotspot"
|
||||
* </pre>
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* <pre>
|
||||
* -Djvmci.ForceTranslateFailure=nmethod/StackOverflowError:native,method/computeHash,execute
|
||||
* </pre>
|
||||
*
|
||||
* will cause failure of:
|
||||
* <ul>
|
||||
* <li>translating a {@link HotSpotNmethod} to the libjvmci heap whose fully qualified name
|
||||
* contains "StackOverflowError"</li>
|
||||
* <li>translating a {@link HotSpotResolvedJavaMethodImpl} to the libjvmci or HotSpot heap whose
|
||||
* fully qualified name contains "computeHash"</li>
|
||||
* <li>translating a {@link HotSpotNmethod}, {@link HotSpotResolvedJavaMethodImpl} or
|
||||
* {@link HotSpotResolvedObjectTypeImpl} to the libjvmci or HotSpot heap whose fully qualified
|
||||
* name contains "execute"</li>
|
||||
* </ul>
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static void postTranslation(Object translatedObject) {
|
||||
String value = Option.ForceTranslateFailure.getString();
|
||||
String toMatch;
|
||||
String type;
|
||||
if (translatedObject instanceof HotSpotResolvedJavaMethodImpl) {
|
||||
toMatch = ((HotSpotResolvedJavaMethodImpl) translatedObject).format("%H.%n");
|
||||
type = "method";
|
||||
} else if (translatedObject instanceof HotSpotResolvedObjectTypeImpl) {
|
||||
toMatch = ((HotSpotResolvedObjectTypeImpl) translatedObject).toJavaName();
|
||||
type = "type";
|
||||
} else if (translatedObject instanceof HotSpotNmethod) {
|
||||
HotSpotNmethod nmethod = (HotSpotNmethod) translatedObject;
|
||||
if (nmethod.getMethod() != null) {
|
||||
toMatch = nmethod.getMethod().format("%H.%n");
|
||||
} else {
|
||||
toMatch = String.valueOf(nmethod.getName());
|
||||
}
|
||||
type = "nmethod";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
String[] filters = value.split(",");
|
||||
for (String filter : filters) {
|
||||
Matcher m = FORCE_TRANSLATE_FAILURE_FILTER_RE.matcher(filter);
|
||||
if (!m.matches()) {
|
||||
throw new JVMCIError(Option.ForceTranslateFailure + " filter does not match " + FORCE_TRANSLATE_FAILURE_FILTER_RE + ": " + filter);
|
||||
}
|
||||
String typeSelector = m.group(1);
|
||||
String substring = m.group(2);
|
||||
String jvmciEnvSelector = m.group(3);
|
||||
if (jvmciEnvSelector != null) {
|
||||
if (jvmciEnvSelector.equals("native")) {
|
||||
if (!Services.IS_IN_NATIVE_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (Services.IS_IN_NATIVE_IMAGE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeSelector != null && !typeSelector.equals(type)) {
|
||||
continue;
|
||||
}
|
||||
if (toMatch.contains(substring)) {
|
||||
throw new JVMCIError("translation of " + translatedObject + " failed due to matching " + Option.ForceTranslateFailure + " filter \"" + filter + "\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,8 +68,8 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
|
||||
|
||||
/**
|
||||
* If this field is 0, this object is in the oops table of the nmethod. Otherwise, the value of
|
||||
* the field records the nmethod's compile identifier. This value is used to confirm an entry in
|
||||
* the code cache retrieved by {@link #address} is indeed the nmethod represented by this
|
||||
* the field records the nmethod's compile identifier. This value is used to confirm if an entry
|
||||
* in the code cache retrieved by {@link #address} is indeed the nmethod represented by this
|
||||
* object.
|
||||
*
|
||||
* @see #inOopsTable
|
||||
@@ -85,6 +85,23 @@ public class HotSpotNmethod extends HotSpotInstalledCode {
|
||||
assert inOopsTable || compileId != 0L : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches {@code log} to this object. If {@code log.managesFailedSpeculations() == true}, this
|
||||
* ensures the failed speculation list lives at least as long as this object.
|
||||
*/
|
||||
public void setSpeculationLog(HotSpotSpeculationLog log) {
|
||||
this.speculationLog = log;
|
||||
}
|
||||
|
||||
/**
|
||||
* The speculation log containing speculations embedded in the nmethod.
|
||||
*
|
||||
* If {@code speculationLog.managesFailedSpeculations() == true}, this field ensures the failed
|
||||
* speculation list lives at least as long as this object. This prevents deoptimization from
|
||||
* appending to an already freed list.
|
||||
*/
|
||||
@SuppressWarnings("unused") private HotSpotSpeculationLog speculationLog;
|
||||
|
||||
/**
|
||||
* Determines if the nmethod associated with this object is the compiled entry point for
|
||||
* {@link #getMethod()}.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2022, 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
|
||||
@@ -22,13 +22,20 @@
|
||||
*/
|
||||
package jdk.vm.ci.hotspot;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Formatter;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import jdk.vm.ci.common.JVMCIError;
|
||||
|
||||
/**
|
||||
* Support for translating exceptions between different runtime heaps.
|
||||
@@ -36,6 +43,26 @@ import java.util.Objects;
|
||||
@SuppressWarnings("serial")
|
||||
final class TranslatedException extends Exception {
|
||||
|
||||
/**
|
||||
* The value returned by {@link #encodeThrowable(Throwable)} when encoding fails due to an
|
||||
* {@link OutOfMemoryError}.
|
||||
*/
|
||||
private static final byte[] FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES;
|
||||
|
||||
/**
|
||||
* The value returned by {@link #encodeThrowable(Throwable)} when encoding fails for any reason
|
||||
* other than {@link OutOfMemoryError}.
|
||||
*/
|
||||
private static final byte[] FALLBACK_ENCODED_THROWABLE_BYTES;
|
||||
static {
|
||||
try {
|
||||
FALLBACK_ENCODED_THROWABLE_BYTES = encodeThrowable(new TranslatedException("error during encoding", "<unknown>"), false);
|
||||
FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES = encodeThrowable(new OutOfMemoryError(), false);
|
||||
} catch (IOException e) {
|
||||
throw new JVMCIError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class name of exception that could not be instantiated.
|
||||
*/
|
||||
@@ -110,83 +137,74 @@ final class TranslatedException extends Exception {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes an exception message to distinguish a null message from an empty message.
|
||||
*
|
||||
* @return {@code value} with a space prepended iff {@code value != null}
|
||||
*/
|
||||
private static String encodeMessage(String value) {
|
||||
return value != null ? ' ' + value : value;
|
||||
private static String emptyIfNull(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
private static String decodeMessage(String value) {
|
||||
if (value.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return value.substring(1);
|
||||
}
|
||||
|
||||
private static String encodedString(String value) {
|
||||
return Objects.toString(value, "").replace('|', '_');
|
||||
private static String emptyAsNull(String value) {
|
||||
return value.isEmpty() ? null : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code throwable} including its stack and causes as a string. The encoding format of
|
||||
* a single exception is:
|
||||
*
|
||||
* <pre>
|
||||
* <exception class name> '|' <exception message> '|' <stack size> '|' [ <classLoader> '|' <module> '|' <moduleVersion> '|' <class> '|' <method> '|' <file> '|' <line> '|' ]*
|
||||
* </pre>
|
||||
*
|
||||
* Each exception is encoded before the exception it causes.
|
||||
* Encodes {@code throwable} including its stack and causes as a {@linkplain GZIPOutputStream
|
||||
* compressed} byte array that can be decoded by {@link #decodeThrowable}.
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static String encodeThrowable(Throwable throwable) throws Throwable {
|
||||
static byte[] encodeThrowable(Throwable throwable) throws Throwable {
|
||||
try {
|
||||
Formatter enc = new Formatter();
|
||||
return encodeThrowable(throwable, true);
|
||||
} catch (OutOfMemoryError e) {
|
||||
return FALLBACK_ENCODED_OUTOFMEMORYERROR_BYTES;
|
||||
} catch (Throwable e) {
|
||||
return FALLBACK_ENCODED_THROWABLE_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] encodeThrowable(Throwable throwable, boolean withCauseAndStack) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(baos))) {
|
||||
List<Throwable> throwables = new ArrayList<>();
|
||||
for (Throwable current = throwable; current != null; current = current.getCause()) {
|
||||
throwables.add(current);
|
||||
if (!withCauseAndStack) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode from inner most cause outwards
|
||||
Collections.reverse(throwables);
|
||||
|
||||
for (Throwable current : throwables) {
|
||||
enc.format("%s|%s|", current.getClass().getName(), encodedString(encodeMessage(current.getMessage())));
|
||||
StackTraceElement[] stackTrace = current.getStackTrace();
|
||||
dos.writeUTF(current.getClass().getName());
|
||||
dos.writeUTF(emptyIfNull(current.getMessage()));
|
||||
StackTraceElement[] stackTrace = withCauseAndStack ? current.getStackTrace() : null;
|
||||
if (stackTrace == null) {
|
||||
stackTrace = new StackTraceElement[0];
|
||||
}
|
||||
enc.format("%d|", stackTrace.length);
|
||||
dos.writeInt(stackTrace.length);
|
||||
for (int i = 0; i < stackTrace.length; i++) {
|
||||
StackTraceElement frame = stackTrace[i];
|
||||
if (frame != null) {
|
||||
enc.format("%s|%s|%s|%s|%s|%s|%d|", encodedString(frame.getClassLoaderName()),
|
||||
encodedString(frame.getModuleName()), encodedString(frame.getModuleVersion()),
|
||||
frame.getClassName(), frame.getMethodName(),
|
||||
encodedString(frame.getFileName()), frame.getLineNumber());
|
||||
dos.writeUTF(emptyIfNull(frame.getClassLoaderName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getModuleName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getModuleVersion()));
|
||||
dos.writeUTF(emptyIfNull(frame.getClassName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getMethodName()));
|
||||
dos.writeUTF(emptyIfNull(frame.getFileName()));
|
||||
dos.writeInt(frame.getLineNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
return enc.toString();
|
||||
} catch (Throwable e) {
|
||||
assert printStackTrace(e);
|
||||
try {
|
||||
return e.getClass().getName() + "|" + encodedString(e.getMessage()) + "|0|";
|
||||
} catch (Throwable e2) {
|
||||
assert printStackTrace(e2);
|
||||
return "java.lang.Throwable|too many errors during encoding|0|";
|
||||
}
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stack of the current thread without the frames between this call and the one just
|
||||
* below the frame of the first method in {@link CompilerToVM}. The chopped frames are specific
|
||||
* to the implementation of {@link HotSpotJVMCIRuntime#decodeThrowable(String)}.
|
||||
* below the frame of the first method in {@link CompilerToVM}. The chopped frames are for the
|
||||
* VM call to {@link HotSpotJVMCIRuntime#decodeAndThrowThrowable}.
|
||||
*/
|
||||
private static StackTraceElement[] getStackTraceSuffix() {
|
||||
private static StackTraceElement[] getMyStackTrace() {
|
||||
StackTraceElement[] stack = new Exception().getStackTrace();
|
||||
for (int i = 0; i < stack.length; i++) {
|
||||
StackTraceElement e = stack[i];
|
||||
@@ -206,43 +224,47 @@ final class TranslatedException extends Exception {
|
||||
* {@link #encodeThrowable}
|
||||
*/
|
||||
@VMEntryPoint
|
||||
static Throwable decodeThrowable(String encodedThrowable) {
|
||||
try {
|
||||
int i = 0;
|
||||
String[] parts = encodedThrowable.split("\\|");
|
||||
static Throwable decodeThrowable(byte[] encodedThrowable) {
|
||||
try (DataInputStream dis = new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(encodedThrowable)))) {
|
||||
Throwable cause = null;
|
||||
Throwable throwable = null;
|
||||
while (i != parts.length) {
|
||||
String exceptionClassName = parts[i++];
|
||||
String exceptionMessage = decodeMessage(parts[i++]);
|
||||
StackTraceElement[] myStack = getMyStackTrace();
|
||||
while (dis.available() != 0) {
|
||||
String exceptionClassName = dis.readUTF();
|
||||
String exceptionMessage = emptyAsNull(dis.readUTF());
|
||||
throwable = create(exceptionClassName, exceptionMessage, cause);
|
||||
int stackTraceDepth = Integer.parseInt(parts[i++]);
|
||||
|
||||
StackTraceElement[] suffix = getStackTraceSuffix();
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
|
||||
int stackTraceDepth = dis.readInt();
|
||||
StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + myStack.length];
|
||||
int stackTraceIndex = 0;
|
||||
int myStackIndex = 0;
|
||||
for (int j = 0; j < stackTraceDepth; j++) {
|
||||
String classLoaderName = parts[i++];
|
||||
String moduleName = parts[i++];
|
||||
String moduleVersion = parts[i++];
|
||||
String className = parts[i++];
|
||||
String methodName = parts[i++];
|
||||
String fileName = parts[i++];
|
||||
int lineNumber = Integer.parseInt(parts[i++]);
|
||||
if (classLoaderName.isEmpty()) {
|
||||
classLoaderName = null;
|
||||
String classLoaderName = emptyAsNull(dis.readUTF());
|
||||
String moduleName = emptyAsNull(dis.readUTF());
|
||||
String moduleVersion = emptyAsNull(dis.readUTF());
|
||||
String className = emptyAsNull(dis.readUTF());
|
||||
String methodName = emptyAsNull(dis.readUTF());
|
||||
String fileName = emptyAsNull(dis.readUTF());
|
||||
int lineNumber = dis.readInt();
|
||||
StackTraceElement ste = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName, lineNumber);
|
||||
|
||||
if (ste.isNativeMethod()) {
|
||||
// Best effort attempt to weave stack traces from two heaps into
|
||||
// a single stack trace using native method frames as stitching points.
|
||||
// This is not 100% reliable as there's no guarantee that native method
|
||||
// frames only exist for calls between HotSpot and libjvmci.
|
||||
while (myStackIndex < myStack.length) {
|
||||
StackTraceElement suffixSTE = myStack[myStackIndex++];
|
||||
if (suffixSTE.isNativeMethod()) {
|
||||
break;
|
||||
}
|
||||
stackTrace[stackTraceIndex++] = suffixSTE;
|
||||
}
|
||||
}
|
||||
if (moduleName.isEmpty()) {
|
||||
moduleName = null;
|
||||
}
|
||||
if (moduleVersion.isEmpty()) {
|
||||
moduleVersion = null;
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = null;
|
||||
}
|
||||
stackTrace[j] = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName, lineNumber);
|
||||
stackTrace[stackTraceIndex++] = ste;
|
||||
}
|
||||
while (myStackIndex < myStack.length) {
|
||||
stackTrace[stackTraceIndex++] = myStack[myStackIndex++];
|
||||
}
|
||||
System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
|
||||
throwable.setStackTrace(stackTrace);
|
||||
cause = throwable;
|
||||
}
|
||||
|
||||
@@ -154,6 +154,8 @@ public interface EventStream extends AutoCloseable {
|
||||
* <p>
|
||||
* By default, the stream starts with the next event flushed by Flight
|
||||
* Recorder.
|
||||
* <p>
|
||||
* Only trusted disk repositories should be opened.
|
||||
*
|
||||
* @param directory location of the disk repository, not {@code null}
|
||||
*
|
||||
@@ -184,6 +186,8 @@ public interface EventStream extends AutoCloseable {
|
||||
* Creates an event stream from a file.
|
||||
* <p>
|
||||
* By default, the stream starts with the first event in the file.
|
||||
* <p>
|
||||
* Only recording files from trusted sources should be opened.
|
||||
*
|
||||
* @param file location of the file, not {@code null}
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2021, 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
|
||||
@@ -72,6 +72,8 @@ public final class RecordingFile implements Closeable {
|
||||
|
||||
/**
|
||||
* Creates a recording file.
|
||||
* <p>
|
||||
* Only recording files from trusted sources should be used.
|
||||
*
|
||||
* @param file the path of the file to open, not {@code null}
|
||||
* @throws IOException if it's not a valid recording file, or an I/O error
|
||||
@@ -211,6 +213,8 @@ public final class RecordingFile implements Closeable {
|
||||
* <p>
|
||||
* This method is intended for simple cases where it's convenient to read all
|
||||
* events in a single operation. It isn't intended for reading large files.
|
||||
* <p>
|
||||
* Only recording files from trusted sources should be used.
|
||||
*
|
||||
* @param path the path to the file, not {@code null}
|
||||
*
|
||||
|
||||
@@ -66,6 +66,7 @@ final class MetadataReader {
|
||||
public MetadataReader(RecordingInput input) throws IOException {
|
||||
this.input = input;
|
||||
int size = input.readInt();
|
||||
input.require(size, "Metadata string pool size %d exceeds available data" );
|
||||
this.pool = new ArrayList<>(size);
|
||||
StringParser p = new StringParser(null, false);
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2021, 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
|
||||
@@ -114,7 +114,7 @@ public final class ChunkHeader {
|
||||
byte fileState1;
|
||||
input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
|
||||
while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) {
|
||||
Utils.takeNap(1);
|
||||
input.pollWait();
|
||||
input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
|
||||
}
|
||||
input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION);
|
||||
@@ -184,7 +184,7 @@ public final class ChunkHeader {
|
||||
finished = true;
|
||||
return;
|
||||
}
|
||||
Utils.takeNap(1);
|
||||
input.pollWait();
|
||||
}
|
||||
} finally {
|
||||
input.position(pos);
|
||||
|
||||
@@ -148,6 +148,7 @@ public class EventDirectoryStream extends AbstractEventStream {
|
||||
}
|
||||
currentChunkStartNanos = repositoryFiles.getTimestamp(path);
|
||||
try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
|
||||
input.setStreamed();
|
||||
currentParser = new ChunkParser(input, disp.parserConfiguration, parserState);
|
||||
long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration();
|
||||
long filterStart = validStartTime ? disp.startNanos : segmentStart;
|
||||
|
||||
@@ -50,6 +50,7 @@ public final class EventFileStream extends AbstractEventStream {
|
||||
super(acc, null, Collections.emptyList());
|
||||
Objects.requireNonNull(path);
|
||||
this.input = new RecordingInput(path.toFile(), FileAccess.UNPRIVILEGED);
|
||||
this.input.setStreamed();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -214,6 +214,7 @@ public final class OngoingStream extends EventByteStream {
|
||||
return false;
|
||||
}
|
||||
input = new RecordingInput(path.toFile(), SecuritySupport.PRIVILEGED);
|
||||
input.setStreamed();
|
||||
header = new ChunkHeader(input);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -296,6 +296,7 @@ final class ParserFactory {
|
||||
@Override
|
||||
public Object parse(RecordingInput input) throws IOException {
|
||||
final int size = input.readInt();
|
||||
input.require(size, "Array size %d exceeds available data" );
|
||||
final Object[] array = new Object[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
array[i] = elementParser.parse(input);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2021, 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
|
||||
@@ -31,6 +31,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Path;
|
||||
import jdk.jfr.internal.Utils;
|
||||
|
||||
public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
|
||||
@@ -66,6 +67,7 @@ public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
}
|
||||
private final int blockSize;
|
||||
private final FileAccess fileAccess;
|
||||
private long pollCount = 1000;
|
||||
private RandomAccessFile file;
|
||||
private String filename;
|
||||
private Block currentBlock = new Block();
|
||||
@@ -418,6 +420,15 @@ public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
return filename;
|
||||
}
|
||||
|
||||
// Purpose of this method is to prevent OOM by sanity check
|
||||
// the minimum required number of bytes against what is available in
|
||||
// segment/chunk/file
|
||||
public void require(int minimumBytes, String errorMessage) throws IOException {
|
||||
if (position + minimumBytes > size) {
|
||||
throw new IOException(String.format(errorMessage, minimumBytes));
|
||||
}
|
||||
}
|
||||
|
||||
// Purpose of this method is to reuse block cache from a
|
||||
// previous RecordingInput
|
||||
public void setFile(Path path) throws IOException {
|
||||
@@ -430,4 +441,18 @@ public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
initialize(path.toFile());
|
||||
}
|
||||
|
||||
// Marks that it is OK to poll indefinitely for file update
|
||||
// By default, only 1000 polls are allowed
|
||||
public void setStreamed() {
|
||||
this.pollCount = Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
// Wait for file to be updated
|
||||
public void pollWait() throws IOException {
|
||||
pollCount--;
|
||||
if (pollCount < 0) {
|
||||
throw new IOException("Recording file is stuck in locked stream state.");
|
||||
}
|
||||
Utils.takeNap(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ public final class StringParser extends Parser {
|
||||
@Override
|
||||
public Object parse(RecordingInput input) throws IOException {
|
||||
int size = input.readInt();
|
||||
input.require(size, "String size %d exceeds available data");
|
||||
ensureSize(size);
|
||||
if (lastSize == size) {
|
||||
boolean equalsLastString = true;
|
||||
@@ -115,6 +116,7 @@ public final class StringParser extends Parser {
|
||||
@Override
|
||||
public Object parse(RecordingInput input) throws IOException {
|
||||
int size = input.readInt();
|
||||
input.require(size, "String size %d exceeds available data");
|
||||
ensureSize(size);
|
||||
if (lastSize == size) {
|
||||
boolean equalsLastString = true;
|
||||
|
||||
@@ -185,7 +185,7 @@ final class JSONWriter extends EventPrintWriter {
|
||||
private void printDataStructureName(String text) {
|
||||
printIndent();
|
||||
print("\"");
|
||||
print(text);
|
||||
printEscaped(text);
|
||||
print("\": ");
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,11 @@ final class XMLWriter extends EventPrintWriter {
|
||||
}
|
||||
|
||||
private void printAttribute(String name, String value) {
|
||||
print(" ", name, "=\"", value, "\"");
|
||||
print(" ");
|
||||
print(name); // Only known strings
|
||||
print("=\"");
|
||||
printEscaped(value);
|
||||
print("\"");
|
||||
}
|
||||
|
||||
public void printObject(RecordedObject struct) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@@ -27,7 +27,11 @@ package com.sun.jndi.dns;
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Hashtable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Locale;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.sun.jndi.toolkit.url.Uri;
|
||||
@@ -56,6 +60,24 @@ import com.sun.jndi.toolkit.url.UrlUtil;
|
||||
|
||||
public class DnsUrl extends Uri {
|
||||
|
||||
private static final String PARSE_MODE_PROP = "com.sun.jndi.dnsURLParsing";
|
||||
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
||||
|
||||
public static final ParseMode PARSE_MODE;
|
||||
static {
|
||||
PrivilegedAction<String> action = () ->
|
||||
System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
||||
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
||||
try {
|
||||
@SuppressWarnings("removal")
|
||||
String mode = AccessController.doPrivileged(action);
|
||||
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
||||
} catch (Throwable t) {
|
||||
parseMode = DEFAULT_PARSE_MODE;
|
||||
} finally {
|
||||
PARSE_MODE = parseMode;
|
||||
}
|
||||
}
|
||||
private String domain; // domain name of the context
|
||||
|
||||
|
||||
@@ -71,19 +93,58 @@ public class DnsUrl extends Uri {
|
||||
StringTokenizer st = new StringTokenizer(urlList, " ");
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
urls[i++] = new DnsUrl(st.nextToken());
|
||||
try {
|
||||
urls[i++] = new DnsUrl(validateURI(st.nextToken()));
|
||||
} catch (URISyntaxException e) {
|
||||
MalformedURLException mue = new MalformedURLException(e.getMessage());
|
||||
mue.initCause(e);
|
||||
throw mue;
|
||||
}
|
||||
}
|
||||
DnsUrl[] trimmed = new DnsUrl[i];
|
||||
System.arraycopy(urls, 0, trimmed, 0, i);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ParseMode parseMode() {
|
||||
return PARSE_MODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean isSchemeOnly(String uri) {
|
||||
return isDnsSchemeOnly(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkSchemeOnly(String uri, String scheme) {
|
||||
return uri.equals(scheme + ":") || uri.equals(scheme + "://");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final MalformedURLException newInvalidURISchemeException(String uri) {
|
||||
return new MalformedURLException(
|
||||
uri + " is not a valid DNS pseudo-URL");
|
||||
}
|
||||
|
||||
private static boolean isDnsSchemeOnly(String uri) {
|
||||
return "dns:".equals(uri) || "dns://".equals(uri);
|
||||
}
|
||||
|
||||
private static String validateURI(String uri) throws URISyntaxException {
|
||||
// no validation in legacy parsing mode
|
||||
if (PARSE_MODE == ParseMode.LEGACY) return uri;
|
||||
// special case of scheme-only URIs
|
||||
if (isDnsSchemeOnly(uri)) return uri;
|
||||
// use java.net.URI to validate the uri syntax
|
||||
return new URI(uri).toString();
|
||||
}
|
||||
|
||||
public DnsUrl(String url) throws MalformedURLException {
|
||||
super(url);
|
||||
|
||||
if (!scheme.equals("dns")) {
|
||||
throw new MalformedURLException(
|
||||
url + " is not a valid DNS pseudo-URL");
|
||||
throw newInvalidURISchemeException(url);
|
||||
}
|
||||
|
||||
domain = path.startsWith("/")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2022, 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
|
||||
@@ -25,12 +25,17 @@
|
||||
|
||||
package com.sun.jndi.url.rmi;
|
||||
|
||||
import java.net.URI;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.naming.*;
|
||||
import javax.naming.spi.ResolveResult;
|
||||
import com.sun.jndi.toolkit.url.GenericURLContext;
|
||||
import com.sun.jndi.rmi.registry.RegistryContext;
|
||||
import com.sun.jndi.toolkit.url.Uri.ParseMode;
|
||||
|
||||
|
||||
/**
|
||||
@@ -47,10 +52,244 @@ import com.sun.jndi.rmi.registry.RegistryContext;
|
||||
*/
|
||||
public class rmiURLContext extends GenericURLContext {
|
||||
|
||||
private static final String PARSE_MODE_PROP = "com.sun.jndi.rmiURLParsing";
|
||||
private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT;
|
||||
|
||||
public static final ParseMode PARSE_MODE;
|
||||
static {
|
||||
PrivilegedAction<String> action = () ->
|
||||
System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString());
|
||||
ParseMode parseMode = DEFAULT_PARSE_MODE;
|
||||
try {
|
||||
@SuppressWarnings("removal")
|
||||
String mode = AccessController.doPrivileged(action);
|
||||
parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT));
|
||||
} catch (Throwable t) {
|
||||
parseMode = DEFAULT_PARSE_MODE;
|
||||
} finally {
|
||||
PARSE_MODE = parseMode;
|
||||
}
|
||||
}
|
||||
|
||||
public rmiURLContext(Hashtable<?,?> env) {
|
||||
super(env);
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
final String url;
|
||||
final ParseMode mode;
|
||||
String host = null;
|
||||
int port = -1;
|
||||
String objName = null;
|
||||
public Parser(String url) {
|
||||
this(url, PARSE_MODE);
|
||||
}
|
||||
public Parser(String url, ParseMode mode) {
|
||||
this.url = url;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public String url() {return url;}
|
||||
public String host() {return host;}
|
||||
public int port() {return port;}
|
||||
public String objName() {return objName;}
|
||||
public ParseMode mode() {return mode;}
|
||||
|
||||
public void parse() throws NamingException {
|
||||
if (!url.startsWith("rmi:")) {
|
||||
throw (new IllegalArgumentException(
|
||||
"rmiURLContext: name is not an RMI URL: " + url));
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case STRICT -> parseStrict();
|
||||
case COMPAT -> parseCompat();
|
||||
case LEGACY -> parseLegacy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void parseStrict() throws NamingException {
|
||||
assert url.startsWith("rmi:");
|
||||
|
||||
if (url.equals("rmi:") || url.equals("rmi://")) return;
|
||||
|
||||
// index into url, following the "rmi:"
|
||||
int i = 4;
|
||||
|
||||
if (url.startsWith("//", i)) {
|
||||
i += 2;
|
||||
try {
|
||||
URI uri = URI.create(url);
|
||||
host = uri.getHost();
|
||||
port = uri.getPort();
|
||||
String auth = uri.getRawAuthority();
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1 ? "" : ":" + port);
|
||||
if (!hostport.equals(auth)) {
|
||||
boolean failed = true;
|
||||
if (hostport.equals("") && auth.startsWith(":")) {
|
||||
// supports missing host
|
||||
try {
|
||||
port = Integer.parseInt(auth.substring(1));
|
||||
failed = false;
|
||||
} catch (NumberFormatException x) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
throw newNamingException(new IllegalArgumentException("invalid authority: "
|
||||
+ auth));
|
||||
}
|
||||
}
|
||||
i += auth.length();
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw newNamingException(iae);
|
||||
}
|
||||
}
|
||||
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseCompat() throws NamingException {
|
||||
assert url.startsWith("rmi:");
|
||||
|
||||
int i = 4; // index into url, following the "rmi:"
|
||||
boolean hasAuthority = url.startsWith("//", i);
|
||||
if (hasAuthority) i += 2; // skip past "//"
|
||||
int slash = url.indexOf('/', i);
|
||||
int qmark = url.indexOf('?', i);
|
||||
int fmark = url.indexOf('#', i);
|
||||
if (fmark > -1 && qmark > fmark) qmark = -1;
|
||||
if (fmark > -1 && slash > fmark) slash = -1;
|
||||
if (qmark > -1 && slash > qmark) slash = -1;
|
||||
|
||||
// The end of the authority component is either the
|
||||
// slash (slash will be -1 if it doesn't come before
|
||||
// query or fragment), or the question mark (qmark will
|
||||
// be -1 if it doesn't come before the fragment), or
|
||||
// the fragment separator, or the end of the URI
|
||||
// string if there is no path, no query, and no fragment.
|
||||
int enda = slash > -1 ? slash
|
||||
: (qmark > -1 ? qmark
|
||||
: (fmark > -1 ? fmark
|
||||
: url.length()));
|
||||
if (fmark > -1) {
|
||||
if (!acceptsFragment()) {
|
||||
throw newNamingException(new IllegalArgumentException("URI fragments not supported: " + url));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAuthority && enda > i) { // parse "//host:port"
|
||||
if (url.startsWith(":", i)) {
|
||||
// LdapURL supports empty host.
|
||||
i++;
|
||||
host = "";
|
||||
if (enda > i) {
|
||||
port = Integer.parseInt(url.substring(i, enda));
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
URI uri = URI.create(url.substring(0, enda));
|
||||
host = uri.getHost();
|
||||
port = uri.getPort();
|
||||
String hostport = (host == null ? "" : host)
|
||||
+ (port == -1 ? "" : ":" + port);
|
||||
if (!hostport.equals(uri.getRawAuthority())) {
|
||||
throw newNamingException(new IllegalArgumentException("invalid authority: "
|
||||
+ uri.getRawAuthority()));
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw newNamingException(iae);
|
||||
}
|
||||
}
|
||||
i = enda;
|
||||
}
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The legacy parsing used to only throw IllegalArgumentException
|
||||
// and continues to do so
|
||||
private void parseLegacy() {
|
||||
assert url.startsWith("rmi:");
|
||||
|
||||
// Parse the URL.
|
||||
int i = 4; // index into url, following the "rmi:"
|
||||
|
||||
if (url.startsWith("//", i)) { // parse "//host:port"
|
||||
i += 2; // skip past "//"
|
||||
int slash = url.indexOf('/', i);
|
||||
if (slash < 0) {
|
||||
slash = url.length();
|
||||
}
|
||||
if (url.startsWith("[", i)) { // at IPv6 literal
|
||||
int brac = url.indexOf(']', i + 1);
|
||||
if (brac < 0 || brac > slash) {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
host = url.substring(i, brac + 1); // include brackets
|
||||
i = brac + 1; // skip past "[...]"
|
||||
} else { // at host name or IPv4
|
||||
int colon = url.indexOf(':', i);
|
||||
int hostEnd = (colon < 0 || colon > slash)
|
||||
? slash
|
||||
: colon;
|
||||
if (i < hostEnd) {
|
||||
host = url.substring(i, hostEnd);
|
||||
}
|
||||
i = hostEnd; // skip past host
|
||||
}
|
||||
if ((i + 1 < slash)) {
|
||||
if ( url.startsWith(":", i)) { // parse port
|
||||
i++; // skip past ":"
|
||||
port = Integer.parseInt(url.substring(i, slash));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
}
|
||||
i = slash;
|
||||
}
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
}
|
||||
|
||||
NamingException newNamingException(Throwable cause) {
|
||||
NamingException ne = new NamingException(cause.getMessage());
|
||||
ne.initCause(cause);
|
||||
return ne;
|
||||
}
|
||||
|
||||
boolean acceptsFragment() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the registry portion of "url" to the corresponding
|
||||
* RMI registry, and returns the atomic object name as the
|
||||
@@ -59,63 +298,11 @@ public class rmiURLContext extends GenericURLContext {
|
||||
protected ResolveResult getRootURLContext(String url, Hashtable<?,?> env)
|
||||
throws NamingException
|
||||
{
|
||||
if (!url.startsWith("rmi:")) {
|
||||
throw (new IllegalArgumentException(
|
||||
"rmiURLContext: name is not an RMI URL: " + url));
|
||||
}
|
||||
|
||||
// Parse the URL.
|
||||
|
||||
String host = null;
|
||||
int port = -1;
|
||||
String objName = null;
|
||||
|
||||
int i = 4; // index into url, following the "rmi:"
|
||||
|
||||
if (url.startsWith("//", i)) { // parse "//host:port"
|
||||
i += 2; // skip past "//"
|
||||
int slash = url.indexOf('/', i);
|
||||
if (slash < 0) {
|
||||
slash = url.length();
|
||||
}
|
||||
if (url.startsWith("[", i)) { // at IPv6 literal
|
||||
int brac = url.indexOf(']', i + 1);
|
||||
if (brac < 0 || brac > slash) {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
host = url.substring(i, brac + 1); // include brackets
|
||||
i = brac + 1; // skip past "[...]"
|
||||
} else { // at host name or IPv4
|
||||
int colon = url.indexOf(':', i);
|
||||
int hostEnd = (colon < 0 || colon > slash)
|
||||
? slash
|
||||
: colon;
|
||||
if (i < hostEnd) {
|
||||
host = url.substring(i, hostEnd);
|
||||
}
|
||||
i = hostEnd; // skip past host
|
||||
}
|
||||
if ((i + 1 < slash)) {
|
||||
if ( url.startsWith(":", i)) { // parse port
|
||||
i++; // skip past ":"
|
||||
port = Integer.parseInt(url.substring(i, slash));
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"rmiURLContext: name is an Invalid URL: " + url);
|
||||
}
|
||||
}
|
||||
i = slash;
|
||||
}
|
||||
if ("".equals(host)) {
|
||||
host = null;
|
||||
}
|
||||
if (url.startsWith("/", i)) { // skip "/" before object name
|
||||
i++;
|
||||
}
|
||||
if (i < url.length()) {
|
||||
objName = url.substring(i);
|
||||
}
|
||||
Parser parser = new Parser(url);
|
||||
parser.parse();
|
||||
String host = parser.host;
|
||||
int port = parser.port;
|
||||
String objName = parser.objName;
|
||||
|
||||
// Represent object name as empty or single-component composite name.
|
||||
CompositeName remaining = new CompositeName();
|
||||
|
||||
@@ -351,6 +351,23 @@ TEST_VM(os, jio_snprintf) {
|
||||
test_snprintf(jio_snprintf, false);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Not all macOS versions can use os::reserve_memory (i.e. anon_mmap) API
|
||||
// to reserve executable memory, so before attempting to use it,
|
||||
// we need to verify that we can do so by asking for a tiny executable
|
||||
// memory chunk.
|
||||
static inline bool can_reserve_executable_memory(void) {
|
||||
bool executable = true;
|
||||
size_t len = 128;
|
||||
char* p = os::reserve_memory(len, executable);
|
||||
bool exec_supported = (p != NULL);
|
||||
if (exec_supported) {
|
||||
os::release_memory(p, len);
|
||||
}
|
||||
return exec_supported;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Test that os::release_memory() can deal with areas containing multiple mappings.
|
||||
#define PRINT_MAPPINGS(s) { tty->print_cr("%s", s); os::print_memory_mappings((char*)p, total_range_len, tty); }
|
||||
//#define PRINT_MAPPINGS
|
||||
@@ -360,6 +377,13 @@ TEST_VM(os, jio_snprintf) {
|
||||
// (from multiple calls to os::reserve_memory)
|
||||
static address reserve_multiple(int num_stripes, size_t stripe_len) {
|
||||
assert(is_aligned(stripe_len, os::vm_allocation_granularity()), "Sanity");
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Workaround: try reserving executable memory to figure out
|
||||
// if such operation is supported on this macOS version
|
||||
const bool exec_supported = can_reserve_executable_memory();
|
||||
#endif
|
||||
|
||||
size_t total_range_len = num_stripes * stripe_len;
|
||||
// Reserve a large contiguous area to get the address space...
|
||||
address p = (address)os::reserve_memory(total_range_len);
|
||||
@@ -371,7 +395,11 @@ static address reserve_multiple(int num_stripes, size_t stripe_len) {
|
||||
address q = p + (stripe * stripe_len);
|
||||
// Commit, alternatingly with or without exec permission,
|
||||
// to prevent kernel from folding these mappings.
|
||||
#ifdef __APPLE__
|
||||
const bool executable = exec_supported ? (stripe % 2 == 0) : false;
|
||||
#else
|
||||
const bool executable = stripe % 2 == 0;
|
||||
#endif
|
||||
q = (address)os::attempt_reserve_memory_at((char*)q, stripe_len, executable);
|
||||
EXPECT_NE(q, (address)NULL);
|
||||
EXPECT_TRUE(os::commit_memory((char*)q, stripe_len, executable));
|
||||
@@ -413,11 +441,7 @@ struct NUMASwitcher {
|
||||
#endif
|
||||
|
||||
#ifndef _AIX // JDK-8257041
|
||||
#if defined(__APPLE__) && !defined(AARCH64) // See JDK-8267341.
|
||||
TEST_VM(os, DISABLED_release_multi_mappings) {
|
||||
#else
|
||||
TEST_VM(os, release_multi_mappings) {
|
||||
#endif
|
||||
|
||||
// With NMT enabled, this will trigger JDK-8263464. For now disable the test if NMT=on.
|
||||
if (MemTracker::tracking_level() > NMT_off) {
|
||||
|
||||
68
test/hotspot/jtreg/compiler/c2/TestCMoveInfiniteGVN.java
Normal file
68
test/hotspot/jtreg/compiler/c2/TestCMoveInfiniteGVN.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
* @key stress randomness
|
||||
* @bug 8280123
|
||||
* @run main/othervm -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.c2.TestCMoveInfiniteGVN::test
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:StressSeed=43739875
|
||||
* compiler.c2.TestCMoveInfiniteGVN
|
||||
* @run main/othervm -Xcomp -XX:-TieredCompilation
|
||||
* -XX:CompileCommand=compileonly,compiler.c2.TestCMoveInfiniteGVN::test
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN
|
||||
* compiler.c2.TestCMoveInfiniteGVN
|
||||
*/
|
||||
|
||||
package compiler.c2;
|
||||
|
||||
public class TestCMoveInfiniteGVN {
|
||||
|
||||
static int test(boolean b, int i) {
|
||||
int iArr[] = new int[2];
|
||||
|
||||
double d = Math.max(i, i);
|
||||
for (int i1 = 1; i1 < 2; i1++) {
|
||||
if (i1 != 0) {
|
||||
return (b ? 1 : 0); // CMoveI
|
||||
}
|
||||
for (int i2 = 1; i2 < 2; i2++) {
|
||||
switch (i2) {
|
||||
case 1: d -= Math.max(i1, i2); break;
|
||||
}
|
||||
d -= iArr[i1 - 1];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void test() {
|
||||
test(true, 234);
|
||||
}
|
||||
|
||||
public static void main(String[] strArr) {
|
||||
test(); // compilation, then nmethod invalidation during execution
|
||||
test(); // trigger crashing recompilation
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 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
|
||||
@@ -24,7 +24,8 @@
|
||||
/*
|
||||
* @test
|
||||
* @requires vm.jvmci
|
||||
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot:open
|
||||
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot:+open
|
||||
* java.base/jdk.internal.misc
|
||||
* @library /compiler/jvmci/jdk.vm.ci.hotspot.test/src
|
||||
* @run testng/othervm
|
||||
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler
|
||||
@@ -41,6 +42,9 @@ import java.lang.reflect.Method;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
|
||||
|
||||
public class TestTranslatedException {
|
||||
@SuppressWarnings("serial")
|
||||
public static class Untranslatable extends RuntimeException {
|
||||
@@ -56,7 +60,7 @@ public class TestTranslatedException {
|
||||
Class<?> translatedExceptionClass = Class.forName("jdk.vm.ci.hotspot.TranslatedException");
|
||||
|
||||
Method encode = translatedExceptionClass.getDeclaredMethod("encodeThrowable", Throwable.class);
|
||||
Method decode = translatedExceptionClass.getDeclaredMethod("decodeThrowable", String.class);
|
||||
Method decode = translatedExceptionClass.getDeclaredMethod("decodeThrowable", byte[].class);
|
||||
encode.setAccessible(true);
|
||||
decode.setAccessible(true);
|
||||
|
||||
@@ -64,11 +68,50 @@ public class TestTranslatedException {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
|
||||
}
|
||||
String encoding = (String) encode.invoke(null, throwable);
|
||||
byte[] encoding = (byte[]) encode.invoke(null, throwable);
|
||||
Throwable decoded = (Throwable) decode.invoke(null, encoding);
|
||||
assertThrowableEquals(throwable, decoded);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void encodeDecodeTest2() throws Exception {
|
||||
Unsafe unsafe = Unsafe.getUnsafe();
|
||||
int bufferSize = 512;
|
||||
long buffer = 0L;
|
||||
while (true) {
|
||||
buffer = unsafe.allocateMemory(bufferSize);
|
||||
try {
|
||||
Throwable throwable = new ExceptionInInitializerError(new InvocationTargetException(new Untranslatable("test exception", new NullPointerException()), "invoke"));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
|
||||
}
|
||||
|
||||
Method encode = HotSpotJVMCIRuntime.class.getDeclaredMethod("encodeThrowable", Throwable.class, long.class, int.class);
|
||||
Method decode = HotSpotJVMCIRuntime.class.getDeclaredMethod("decodeAndThrowThrowable", long.class);
|
||||
encode.setAccessible(true);
|
||||
decode.setAccessible(true);
|
||||
|
||||
int res = (Integer) encode.invoke(null, throwable, buffer, bufferSize);
|
||||
|
||||
if (res < 0) {
|
||||
bufferSize = -res;
|
||||
} else {
|
||||
try {
|
||||
decode.invoke(null, buffer);
|
||||
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable decoded = e.getCause();
|
||||
assertThrowableEquals(throwable, decoded);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
unsafe.freeMemory(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertThrowableEquals(Throwable original, Throwable decoded) {
|
||||
try {
|
||||
Assert.assertEquals(original == null, decoded == null);
|
||||
|
||||
139
test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java
Normal file
139
test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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
|
||||
* @bug 8277795
|
||||
* @summary Multi-threaded client timeout tests for ldap pool
|
||||
* @library /test/lib
|
||||
* lib/
|
||||
* @run testng/othervm LdapPoolTimeoutTest
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static jdk.test.lib.Utils.adjustTimeout;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.expectThrows;
|
||||
|
||||
public class LdapPoolTimeoutTest {
|
||||
/*
|
||||
* Practical representation of an infinite timeout.
|
||||
*/
|
||||
private static final long INFINITY_MILLIS = adjustTimeout(20_000);
|
||||
/*
|
||||
* The acceptable variation in timeout measurements.
|
||||
*/
|
||||
private static final long TOLERANCE = adjustTimeout( 3_500);
|
||||
|
||||
private static final long CONNECT_MILLIS = adjustTimeout( 3_000);
|
||||
private static final long READ_MILLIS = adjustTimeout(10_000);
|
||||
|
||||
static {
|
||||
// a series of checks to make sure this timeouts configuration is
|
||||
// consistent and the timeouts do not overlap
|
||||
|
||||
assert (TOLERANCE >= 0);
|
||||
// context creation
|
||||
assert (2 * CONNECT_MILLIS + TOLERANCE < READ_MILLIS);
|
||||
// context creation immediately followed by search
|
||||
assert (2 * CONNECT_MILLIS + READ_MILLIS + TOLERANCE < INFINITY_MILLIS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
Hashtable<Object, Object> env = new Hashtable<>();
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS));
|
||||
env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS));
|
||||
env.put("com.sun.jndi.ldap.connect.pool", "true");
|
||||
env.put(Context.PROVIDER_URL, "ldap://example.com:1234");
|
||||
|
||||
try {
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
futures.add(executorService.submit(() -> { attemptConnect(env); return null; }));
|
||||
} finally {
|
||||
executorService.shutdown();
|
||||
}
|
||||
int failedCount = 0;
|
||||
for (var f : futures) {
|
||||
try {
|
||||
f.get();
|
||||
} catch (ExecutionException e) {
|
||||
failedCount++;
|
||||
e.getCause().printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
if (failedCount > 0)
|
||||
throw new RuntimeException(failedCount + " (sub)tests failed");
|
||||
}
|
||||
|
||||
private static void attemptConnect(Hashtable<Object, Object> env) throws Exception {
|
||||
try {
|
||||
LdapTimeoutTest.assertCompletion(CONNECT_MILLIS - 1000,
|
||||
2 * CONNECT_MILLIS + TOLERANCE,
|
||||
() -> new InitialDirContext(env));
|
||||
} catch (RuntimeException e) {
|
||||
String msg = e.getCause() == null ? e.getMessage() : e.getCause().getMessage();
|
||||
System.err.println("MSG RTE: " + msg);
|
||||
// assertCompletion may wrap a CommunicationException in an RTE
|
||||
assertTrue(msg != null && msg.contains("Network is unreachable"));
|
||||
} catch (NamingException ex) {
|
||||
String msg = ex.getCause() == null ? ex.getMessage() : ex.getCause().getMessage();
|
||||
System.err.println("MSG: " + msg);
|
||||
assertTrue(msg != null &&
|
||||
(msg.contains("Network is unreachable")
|
||||
|| msg.contains("Timed out waiting for lock")
|
||||
|| msg.contains("Connect timed out")
|
||||
|| msg.contains("Timeout exceeded while waiting for a connection")));
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
* @bug 8278851
|
||||
* @summary Check that jar entry with at least one non-disabled digest
|
||||
* algorithm in manifest is treated as signed
|
||||
* @modules java.base/sun.security.tools.keytool
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.util.JarUtils
|
||||
* jdk.test.lib.security.SecurityUtils
|
||||
* @run main/othervm JarWithOneNonDisabledDigestAlg
|
||||
*/
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipFile;
|
||||
import jdk.security.jarsigner.JarSigner;
|
||||
|
||||
import jdk.test.lib.util.JarUtils;
|
||||
import jdk.test.lib.security.SecurityUtils;
|
||||
|
||||
public class JarWithOneNonDisabledDigestAlg {
|
||||
|
||||
private static final String PASS = "changeit";
|
||||
private static final String TESTFILE1 = "testfile1";
|
||||
private static final String TESTFILE2 = "testfile2";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SecurityUtils.removeFromDisabledAlgs("jdk.jar.disabledAlgorithms",
|
||||
List.of("SHA1"));
|
||||
Files.write(Path.of(TESTFILE1), TESTFILE1.getBytes());
|
||||
JarUtils.createJarFile(Path.of("unsigned.jar"), Path.of("."),
|
||||
Path.of(TESTFILE1));
|
||||
|
||||
genkeypair("-alias SHA1 -sigalg SHA1withRSA");
|
||||
genkeypair("-alias SHA256 -sigalg SHA256withRSA");
|
||||
|
||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
try (FileInputStream fis = new FileInputStream("keystore")) {
|
||||
ks.load(fis, PASS.toCharArray());
|
||||
}
|
||||
|
||||
// Sign JAR twice with same signer but different digest algorithms
|
||||
// so that each entry in manifest file contains two digest values.
|
||||
signJarFile(ks, "SHA1", "MD5", "unsigned.jar", "signed.jar");
|
||||
signJarFile(ks, "SHA1", "SHA1", "signed.jar", "signed2.jar");
|
||||
checkThatJarIsSigned("signed2.jar", false);
|
||||
|
||||
// add another file to the JAR
|
||||
Files.write(Path.of(TESTFILE2), "testFile2".getBytes());
|
||||
JarUtils.updateJarFile(Path.of("signed2.jar"), Path.of("."),
|
||||
Path.of(TESTFILE2));
|
||||
|
||||
// Sign again with different signer (SHA256) and SHA-1 digestalg.
|
||||
// TESTFILE1 should have two signers and TESTFILE2 should have one
|
||||
// signer.
|
||||
signJarFile(ks, "SHA256", "SHA1", "signed2.jar", "multi-signed.jar");
|
||||
|
||||
checkThatJarIsSigned("multi-signed.jar", true);
|
||||
}
|
||||
|
||||
private static KeyStore.PrivateKeyEntry getEntry(KeyStore ks, String alias)
|
||||
throws Exception {
|
||||
|
||||
return (KeyStore.PrivateKeyEntry)
|
||||
ks.getEntry(alias,
|
||||
new KeyStore.PasswordProtection(PASS.toCharArray()));
|
||||
}
|
||||
|
||||
private static void genkeypair(String cmd) throws Exception {
|
||||
cmd = "-genkeypair -keystore keystore -storepass " + PASS +
|
||||
" -keypass " + PASS + " -keyalg rsa -dname CN=Duke " + cmd;
|
||||
sun.security.tools.keytool.Main.main(cmd.split(" "));
|
||||
}
|
||||
|
||||
private static void signJarFile(KeyStore ks, String alias,
|
||||
String digestAlg, String inputFile, String outputFile)
|
||||
throws Exception {
|
||||
|
||||
JarSigner signer = new JarSigner.Builder(getEntry(ks, alias))
|
||||
.digestAlgorithm(digestAlg)
|
||||
.signerName(alias)
|
||||
.build();
|
||||
|
||||
try (ZipFile in = new ZipFile(inputFile);
|
||||
FileOutputStream out = new FileOutputStream(outputFile)) {
|
||||
signer.sign(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkThatJarIsSigned(String jarFile, boolean multi)
|
||||
throws Exception {
|
||||
|
||||
try (JarFile jf = new JarFile(jarFile, true)) {
|
||||
Enumeration<JarEntry> entries = jf.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (entry.isDirectory() || isSigningRelated(entry.getName())) {
|
||||
continue;
|
||||
}
|
||||
InputStream is = jf.getInputStream(entry);
|
||||
while (is.read() != -1);
|
||||
CodeSigner[] signers = entry.getCodeSigners();
|
||||
if (signers == null) {
|
||||
throw new Exception("JarEntry " + entry.getName() +
|
||||
" is not signed");
|
||||
} else if (multi) {
|
||||
if (entry.getName().equals(TESTFILE1) &&
|
||||
signers.length != 2) {
|
||||
throw new Exception("Unexpected number of signers " +
|
||||
"for " + entry.getName() + ": " + signers.length);
|
||||
} else if (entry.getName().equals(TESTFILE2) &&
|
||||
signers.length != 1) {
|
||||
throw new Exception("Unexpected number of signers " +
|
||||
"for " + entry.getName() + ": " + signers.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSigningRelated(String name) {
|
||||
name = name.toUpperCase(Locale.ENGLISH);
|
||||
if (!name.startsWith("META-INF/")) {
|
||||
return false;
|
||||
}
|
||||
name = name.substring(9);
|
||||
if (name.indexOf('/') != -1) {
|
||||
return false;
|
||||
}
|
||||
return name.endsWith(".SF")
|
||||
|| name.endsWith(".DSA")
|
||||
|| name.endsWith(".RSA")
|
||||
|| name.endsWith(".EC")
|
||||
|| name.equals("MANIFEST.MF");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2022, 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
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
package jdk.test.lib;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@@ -275,5 +276,63 @@ public class SecurityTools {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create a temporary keychain in macOS and use it. The original
|
||||
// keychains will be restored when the object is closed.
|
||||
public static class TemporaryKeychain implements Closeable {
|
||||
// name of new keychain
|
||||
private final String newChain;
|
||||
// names of the original keychains
|
||||
private final List<String> oldChains;
|
||||
|
||||
public TemporaryKeychain(String name) {
|
||||
Path p = Path.of(name + ".keychain-db");
|
||||
newChain = p.toAbsolutePath().toString();
|
||||
try {
|
||||
oldChains = ProcessTools.executeProcess("security", "list-keychains")
|
||||
.shouldHaveExitValue(0)
|
||||
.getStdout()
|
||||
.lines()
|
||||
.map(String::trim)
|
||||
.map(x -> x.startsWith("\"") ? x.substring(1, x.length() - 1) : x)
|
||||
.collect(Collectors.toList());
|
||||
if (!Files.exists(p)) {
|
||||
ProcessTools.executeProcess("security", "create-keychain", "-p", "changeit", newChain)
|
||||
.shouldHaveExitValue(0);
|
||||
}
|
||||
ProcessTools.executeProcess("security", "unlock-keychain", "-p", "changeit", newChain)
|
||||
.shouldHaveExitValue(0);
|
||||
ProcessTools.executeProcess("security", "list-keychains", "-s", newChain)
|
||||
.shouldHaveExitValue(0);
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof RuntimeException re) {
|
||||
throw re;
|
||||
} else {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String chain() {
|
||||
return newChain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
List<String> cmds = new ArrayList<>();
|
||||
cmds.addAll(List.of("security", "list-keychains", "-s"));
|
||||
cmds.addAll(oldChains);
|
||||
try {
|
||||
ProcessTools.executeProcess(cmds.toArray(new String[0]))
|
||||
.shouldHaveExitValue(0);
|
||||
} catch (Throwable t) {
|
||||
if (t instanceof RuntimeException re) {
|
||||
throw re;
|
||||
} else {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user