Skip to content
This repository has been archived by the owner. It is now read-only.

8277964: ClassCastException with no stack trace is thrown with -Xcomp in method handle invocation #27

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -117,9 +117,11 @@ ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) :

if (h_m->method_holder()->is_linked()) {
_can_be_statically_bound = h_m->can_be_statically_bound();
_can_omit_stack_trace = h_m->can_omit_stack_trace();
} else {
// Have to use a conservative value in this case.
_can_be_statically_bound = false;
_can_omit_stack_trace = true;
}

// Adjust the definition of this condition to be more useful:
@@ -176,6 +178,7 @@ ciMethod::ciMethod(ciInstanceKlass* holder,
_intrinsic_id( vmIntrinsics::_none),
_instructions_size(-1),
_can_be_statically_bound(false),
_can_omit_stack_trace(true),
_liveness( NULL)
#if defined(COMPILER2)
,
@@ -766,6 +769,20 @@ bool ciMethod::can_be_statically_bound(ciInstanceKlass* context) const {
return (holder() == context) && can_be_statically_bound();
}

// ------------------------------------------------------------------
// ciMethod::can_omit_stack_trace
//
// Tries to determine whether a method can omit stack trace in throw in compiled code.
bool ciMethod::can_omit_stack_trace() const {
if (!StackTraceInThrowable) {
return true; // stack trace is switched off.
}
if (!OmitStackTraceInFastThrow) {
return false; // Have to provide stack trace.
}
return _can_omit_stack_trace;
}

// ------------------------------------------------------------------
// ciMethod::resolve_invoke
//
@@ -92,6 +92,7 @@ class ciMethod : public ciMetadata {
bool _is_c2_compilable;
bool _can_be_parsed;
bool _can_be_statically_bound;
bool _can_omit_stack_trace;
bool _has_reserved_stack_access;
bool _is_overpass;

@@ -364,6 +365,8 @@ class ciMethod : public ciMetadata {

bool can_be_statically_bound(ciInstanceKlass* context) const;

bool can_omit_stack_trace() const;

// Replay data methods
static void dump_name_as_ascii(outputStream* st, Method* method);
void dump_name_as_ascii(outputStream* st);
@@ -142,6 +142,7 @@
template(java_util_Iterator, "java/util/Iterator") \
template(java_lang_Record, "java/lang/Record") \
template(sun_instrument_InstrumentationImpl, "sun/instrument/InstrumentationImpl") \
template(sun_invoke_util_ValueConversions, "sun/invoke/util/ValueConversions") \
\
template(jdk_internal_loader_NativeLibraries, "jdk/internal/loader/NativeLibraries") \
template(jdk_internal_loader_BuiltinClassLoader, "jdk/internal/loader/BuiltinClassLoader") \
@@ -818,6 +818,18 @@ bool Method::can_be_statically_bound(InstanceKlass* context) const {
return (method_holder() == context) && can_be_statically_bound();
}

/**
* Returns false if this is one of specially treated methods for
* which we have to provide stack trace in throw in compiled code.
* Returns true otherwise.
*/
bool Method::can_omit_stack_trace() {
if (klass_name() == vmSymbols::sun_invoke_util_ValueConversions()) {
return false; // All methods in sun.invoke.util.ValueConversions
}
return true;
}

bool Method::is_accessor() const {
return is_getter() || is_setter();
}
@@ -600,6 +600,9 @@ class Method : public Metadata {
bool can_be_statically_bound(InstanceKlass* context) const;
bool can_be_statically_bound(AccessFlags class_access_flags) const;

// true if method can omit stack trace in throw in compiled code.
bool can_omit_stack_trace();

// returns true if the method has any backward branches.
bool has_loops() {
return access_flags().loops_flag_init() ? access_flags().has_loops() : compute_has_loops_flag();
@@ -560,8 +560,7 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) {
// let us handle the throw inline, with a preconstructed instance.
// Note: If the deopt count has blown up, the uncommon trap
// runtime is going to flush this nmethod, not matter what.
if (treat_throw_as_hot
&& (!StackTraceInThrowable || OmitStackTraceInFastThrow)) {
if (treat_throw_as_hot && method()->can_omit_stack_trace()) {
// If the throw is local, we use a pre-existing instance and
// punt on the backtrace. This would lead to a missing backtrace
// (a repeat of 4292742) if the backtrace object is ever asked
@@ -0,0 +1,111 @@
/*
* 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 8277964
* @summary Test IllegalArgumentException be thrown when an argument is invalid
* @run testng/othervm IllegalArgumentsTest
*/

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.annotations.Test;

public class IllegalArgumentsTest {
static class T {
public T(int i) {}

public static void m(int i) {}

public void m1(String s) {}
}

@Test
public void wrongArgumentType() throws ReflectiveOperationException {
for (int i = 0; i < 100_000; ++i) {
try {
Constructor<T> ctor = T.class.getConstructor(int.class);
ctor.newInstance(int.class); // wrong argument type
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}

for (int i = 0; i < 100_000; ++i) {
try {
Method method = T.class.getMethod("m", int.class);
method.invoke(null, int.class); // wrong argument type
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}
}

@Test
public void nullArguments() throws ReflectiveOperationException {
for (int i = 0; i < 100_000; ++i) {
try {
Constructor<T> ctor = T.class.getConstructor(int.class);
ctor.newInstance(new Object[] {null});
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}

for (int i = 0; i < 100_000; ++i) {
try {
Method method = T.class.getMethod("m", int.class);
method.invoke(null, new Object[] {null});
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}
}

@Test
public void illegalArguments() throws ReflectiveOperationException {
for (int i = 0; i < 100_000; ++i) {
try {
Constructor<T> ctor = T.class.getConstructor(int.class);
ctor.newInstance(new Object[] { 10, 20});
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}

for (int i = 0; i < 100_000; ++i) {
try {
Method method = T.class.getMethod("m", int.class);
method.invoke(null, new Object[] { 10, 20});
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}
}

@Test
public void wrongReceiver() throws ReflectiveOperationException {
for (int i = 0; i < 100_000; ++i) {
try {
Method method = T.class.getMethod("m1", String.class);
method.invoke(this, "bad receiver");
throw new RuntimeException("Expected IAE not thrown");
} catch (IllegalArgumentException e) {}
}
}
}