This commit is contained in:
Thomas Schatzl 2019-11-27 10:21:22 +01:00
commit a5b3bfd8a2
113 changed files with 4070 additions and 232 deletions

View File

@ -190,20 +190,7 @@ AC_DEFUN([FLAGS_SETUP_WARNINGS],
WARNINGS_ENABLE_ALL_CXXFLAGS="$WARNINGS_ENABLE_ALL_CFLAGS $WARNINGS_ENABLE_ADDITIONAL_CXX"
DISABLED_WARNINGS="unused-parameter unused"
# Repeate the check for the BUILD_CC and BUILD_CXX. Need to also reset
# CFLAGS since any target specific flags will likely not work with the
# build compiler
CC_OLD="$CC"
CXX_OLD="$CXX"
CC="$BUILD_CC"
CXX="$BUILD_CXX"
CFLAGS_OLD="$CFLAGS"
CFLAGS=""
BUILD_CC_DISABLE_WARNING_PREFIX="-Wno-"
CC="$CC_OLD"
CXX="$CXX_OLD"
CFLAGS="$CFLAGS_OLD"
;;
clang)
@ -420,6 +407,17 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS],
FLAGS_SETUP_CFLAGS_CPU_DEP([TARGET])
# Repeat the check for the BUILD_CC and BUILD_CXX. Need to also reset CFLAGS
# since any target specific flags will likely not work with the build compiler.
CC_OLD="$CC"
CXX_OLD="$CXX"
CFLAGS_OLD="$CFLAGS"
CXXFLAGS_OLD="$CXXFLAGS"
CC="$BUILD_CC"
CXX="$BUILD_CXX"
CFLAGS=""
CXXFLAGS=""
FLAGS_OS=$OPENJDK_BUILD_OS
FLAGS_OS_TYPE=$OPENJDK_BUILD_OS_TYPE
FLAGS_CPU=$OPENJDK_BUILD_CPU
@ -430,6 +428,11 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS],
FLAGS_CPU_LEGACY_LIB=$OPENJDK_BUILD_CPU_LEGACY_LIB
FLAGS_SETUP_CFLAGS_CPU_DEP([BUILD], [OPENJDK_BUILD_], [BUILD_])
CC="$CC_OLD"
CXX="$CXX_OLD"
CFLAGS="$CFLAGS_OLD"
CXXFLAGS="$CXXFLAGS_OLD"
])
################################################################################
@ -529,6 +532,11 @@ AC_DEFUN([FLAGS_SETUP_CFLAGS_HELPER],
if test "x$TOOLCHAIN_TYPE" = xgcc; then
TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -fcheck-new -fstack-protector"
TOOLCHAIN_CFLAGS_JDK="-pipe -fstack-protector"
# reduce lib size on s390x in link step, this needs also special compile flags
if test "x$OPENJDK_TARGET_CPU" = xs390x; then
TOOLCHAIN_CFLAGS_JVM="$TOOLCHAIN_CFLAGS_JVM -ffunction-sections -fdata-sections"
TOOLCHAIN_CFLAGS_JDK="$TOOLCHAIN_CFLAGS_JDK -ffunction-sections -fdata-sections"
fi
# technically NOT for CXX (but since this gives *worse* performance, use
# no-strict-aliasing everywhere!)
TOOLCHAIN_CFLAGS_JDK_CONLY="-fno-strict-aliasing"

View File

@ -70,10 +70,14 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
fi
# Add -z defs, to forbid undefined symbols in object files.
BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,-z,defs"
BASIC_LDFLAGS_JVM_ONLY="-Wl,-O1 -Wl,-z,relro"
# add relro (mark relocations read only) for all libs
BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,-z,defs -Wl,-z,relro"
# s390x : remove unused code+data in link step
if test "x$OPENJDK_TARGET_CPU" = xs390x; then
BASIC_LDFLAGS="$BASIC_LDFLAGS -Wl,--gc-sections -Wl,--print-gc-sections"
fi
BASIC_LDFLAGS_JVM_ONLY="-Wl,-O1"
elif test "x$TOOLCHAIN_TYPE" = xclang; then
BASIC_LDFLAGS_JVM_ONLY="-mno-omit-leaf-frame-pointer -mstack-alignment=16 \
@ -120,9 +124,6 @@ AC_DEFUN([FLAGS_SETUP_LDFLAGS_HELPER],
if test "x$OPENJDK_TARGET_OS" = xlinux; then
if test x$DEBUG_LEVEL = xrelease; then
DEBUGLEVEL_LDFLAGS_JDK_ONLY="$DEBUGLEVEL_LDFLAGS_JDK_ONLY -Wl,-O1"
else
# mark relocations read only on (fast/slow) debug builds
DEBUGLEVEL_LDFLAGS_JDK_ONLY="-Wl,-z,relro"
fi
if test x$DEBUG_LEVEL = xslowdebug; then
# do relocations at load

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, 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
@ -327,24 +327,42 @@ void BarrierSetAssembler::incr_allocated_bytes(MacroAssembler* masm, Register th
#endif
}
#ifdef _LP64
void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) {
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
if (bs_nm == NULL) {
return;
}
#ifndef _LP64
ShouldNotReachHere();
#else
Label continuation;
Register thread = LP64_ONLY(r15_thread);
Register thread = r15_thread;
Address disarmed_addr(thread, in_bytes(bs_nm->thread_disarmed_offset()));
__ align(8);
__ cmpl(disarmed_addr, 0);
__ jcc(Assembler::equal, continuation);
__ call(RuntimeAddress(StubRoutines::x86::method_entry_barrier()));
__ bind(continuation);
#endif
}
#else
void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) {
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
if (bs_nm == NULL) {
return;
}
Label continuation;
Register tmp = rdi;
__ push(tmp);
__ movptr(tmp, (intptr_t)bs_nm->disarmed_value_address());
Address disarmed_addr(tmp, 0);
__ align(4);
__ cmpl(disarmed_addr, 0);
__ pop(tmp);
__ jcc(Assembler::equal, continuation);
__ call(RuntimeAddress(StubRoutines::x86::method_entry_barrier()));
__ bind(continuation);
}
#endif
void BarrierSetAssembler::c2i_entry_barrier(MacroAssembler* masm) {
BarrierSetNMethod* bs = BarrierSet::barrier_set()->barrier_set_nmethod();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, 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
@ -35,6 +35,7 @@
class NativeNMethodCmpBarrier: public NativeInstruction {
public:
#ifdef _LP64
enum Intel_specific_constants {
instruction_code = 0x81,
instruction_size = 8,
@ -42,6 +43,14 @@ public:
instruction_rex_prefix = Assembler::REX | Assembler::REX_B,
instruction_modrm = 0x7f // [r15 + offset]
};
#else
enum Intel_specific_constants {
instruction_code = 0x81,
instruction_size = 7,
imm_offset = 2,
instruction_modrm = 0x3f // [rdi]
};
#endif
address instruction_address() const { return addr_at(0); }
address immediate_address() const { return addr_at(imm_offset); }
@ -51,6 +60,7 @@ public:
void verify() const;
};
#ifdef _LP64
void NativeNMethodCmpBarrier::verify() const {
if (((uintptr_t) instruction_address()) & 0x7) {
fatal("Not properly aligned");
@ -77,6 +87,27 @@ void NativeNMethodCmpBarrier::verify() const {
fatal("not a cmp barrier");
}
}
#else
void NativeNMethodCmpBarrier::verify() const {
if (((uintptr_t) instruction_address()) & 0x3) {
fatal("Not properly aligned");
}
int inst = ubyte_at(0);
if (inst != instruction_code) {
tty->print_cr("Addr: " INTPTR_FORMAT " Code: 0x%x", p2i(instruction_address()),
inst);
fatal("not a cmp barrier");
}
int modrm = ubyte_at(1);
if (modrm != instruction_modrm) {
tty->print_cr("Addr: " INTPTR_FORMAT " mod/rm: 0x%x", p2i(instruction_address()),
modrm);
fatal("not a cmp barrier");
}
}
#endif // _LP64
void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
/*
@ -127,7 +158,7 @@ void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
// NativeNMethodCmpBarrier::verify() will immediately complain when it does
// not find the expected native instruction at this offset, which needs updating.
// Note that this offset is invariant of PreserveFramePointer.
static const int entry_barrier_offset = -19;
static const int entry_barrier_offset = LP64_ONLY(-19) NOT_LP64(-18);
static NativeNMethodCmpBarrier* native_nmethod_barrier(nmethod* nm) {
address barrier_address = nm->code_begin() + nm->frame_complete_offset() + entry_barrier_offset;

View File

@ -511,6 +511,7 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d
// 3: apply keep-alive barrier if needed
if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) {
__ push_IU_state();
const Register thread = NOT_LP64(tmp_thread) LP64_ONLY(r15_thread);
assert_different_registers(dst, tmp1, tmp_thread);
NOT_LP64(__ get_thread(thread));
@ -523,6 +524,7 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d
tmp1 /* tmp */,
true /* tosca_live */,
true /* expand_call */);
__ pop_IU_state();
}
}

View File

@ -975,6 +975,9 @@ AdapterHandlerEntry* SharedRuntime::generate_i2c2i_adapters(MacroAssembler *masm
address c2i_entry = __ pc();
BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
bs->c2i_entry_barrier(masm);
gen_c2i_adapter(masm, total_args_passed, comp_args_on_stack, sig_bt, regs, skip_fixup);
__ flush();
@ -1886,6 +1889,10 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
// -2 because return address is already present and so is saved rbp
__ subptr(rsp, stack_size - 2*wordSize);
BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
bs->nmethod_entry_barrier(masm);
// Frame is now completed as far as size and linkage.
int frame_complete = ((intptr_t)__ pc()) - start;
@ -1921,12 +1928,12 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
// if we load it once it is usable thru the entire wrapper
const Register thread = rdi;
// We use rsi as the oop handle for the receiver/klass
// It is callee save so it survives the call to native
// We use rsi as the oop handle for the receiver/klass
// It is callee save so it survives the call to native
const Register oop_handle_reg = rsi;
const Register oop_handle_reg = rsi;
__ get_thread(thread);
__ get_thread(thread);
if (is_critical_native && !Universe::heap()->supports_object_pinning()) {
check_needs_gc_for_critical_native(masm, thread, stack_slots, total_c_args, total_in_args,

View File

@ -27,6 +27,7 @@
#include "asm/macroAssembler.inline.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "gc/shared/barrierSetNMethod.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/universe.hpp"
#include "nativeInst_x86.hpp"
@ -3663,6 +3664,68 @@ class StubGenerator: public StubCodeGenerator {
__ ret(0);
}
address generate_method_entry_barrier() {
__ align(CodeEntryAlignment);
StubCodeMark mark(this, "StubRoutines", "nmethod_entry_barrier");
Label deoptimize_label;
address start = __ pc();
__ push(-1); // cookie, this is used for writing the new rsp when deoptimizing
BLOCK_COMMENT("Entry:");
__ enter(); // save rbp
// save rbx, because we want to use that value.
// We could do without it but then we depend on the number of slots used by pusha
__ push(rbx);
__ lea(rbx, Address(rsp, wordSize * 3)); // 1 for cookie, 1 for rbp, 1 for rbx - this should be the return address
__ pusha();
// xmm0 and xmm1 may be used for passing float/double arguments
const int xmm_size = wordSize * 2;
const int xmm_spill_size = xmm_size * 2;
__ subptr(rsp, xmm_spill_size);
__ movdqu(Address(rsp, xmm_size * 1), xmm1);
__ movdqu(Address(rsp, xmm_size * 0), xmm0);
__ call_VM_leaf(CAST_FROM_FN_PTR(address, static_cast<int (*)(address*)>(BarrierSetNMethod::nmethod_stub_entry_barrier)), rbx);
__ movdqu(xmm0, Address(rsp, xmm_size * 0));
__ movdqu(xmm1, Address(rsp, xmm_size * 1));
__ addptr(rsp, xmm_spill_size);
__ cmpl(rax, 1); // 1 means deoptimize
__ jcc(Assembler::equal, deoptimize_label);
__ popa();
__ pop(rbx);
__ leave();
__ addptr(rsp, 1 * wordSize); // cookie
__ ret(0);
__ BIND(deoptimize_label);
__ popa();
__ pop(rbx);
__ leave();
// this can be taken out, but is good for verification purposes. getting a SIGSEGV
// here while still having a correct stack is valuable
__ testptr(rsp, Address(rsp, 0));
__ movptr(rsp, Address(rsp, 0)); // new rsp was written in the barrier
__ jmp(Address(rsp, -1 * wordSize)); // jmp target should be callers verified_entry_point
return start;
}
public:
// Information about frame layout at time of blocking runtime call.
// Note that we only have to preserve callee-saved registers since
@ -3959,6 +4022,11 @@ class StubGenerator: public StubCodeGenerator {
StubRoutines::_safefetchN_entry = StubRoutines::_safefetch32_entry;
StubRoutines::_safefetchN_fault_pc = StubRoutines::_safefetch32_fault_pc;
StubRoutines::_safefetchN_continuation_pc = StubRoutines::_safefetch32_continuation_pc;
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
if (bs_nm != NULL) {
StubRoutines::x86::_method_entry_barrier = generate_method_entry_barrier();
}
}

View File

@ -55,14 +55,8 @@ class x86 {
static address _double_sign_mask;
static address _double_sign_flip;
static address _method_entry_barrier;
public:
static address method_entry_barrier() {
return _method_entry_barrier;
}
static address get_previous_fp_entry() {
return _get_previous_fp_entry;
}
@ -121,6 +115,8 @@ class x86 {
//shuffle mask for big-endian 128-bit integers
static address _counter_shuffle_mask_addr;
static address _method_entry_barrier;
// masks and table for CRC32
static uint64_t _crc_by128_masks[];
static juint _crc_table[];
@ -221,6 +217,7 @@ class x86 {
static address upper_word_mask_addr() { return _upper_word_mask_addr; }
static address shuffle_byte_flip_mask_addr() { return _shuffle_byte_flip_mask_addr; }
static address k256_addr() { return _k256_adr; }
static address method_entry_barrier() { return _method_entry_barrier; }
static address vector_short_to_byte_mask() {
return _vector_short_to_byte_mask;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2019, 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,3 +32,5 @@
// a description of how to extend it, see the stubRoutines.hpp file.
address StubRoutines::x86::_verify_fpu_cntrl_wrd_entry = NULL;
address StubRoutines::x86::_method_entry_barrier = NULL;

View File

@ -367,26 +367,29 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
//
intx saved_useavx = UseAVX;
intx saved_usesse = UseSSE;
// check _cpuid_info.sef_cpuid7_ebx.bits.avx512f
__ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset())));
__ movl(rax, 0x10000);
__ andl(rax, Address(rsi, 4)); // xcr0 bits sse | ymm
__ cmpl(rax, 0x10000);
__ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported
// check _cpuid_info.xem_xcr0_eax.bits.opmask
// check _cpuid_info.xem_xcr0_eax.bits.zmm512
// check _cpuid_info.xem_xcr0_eax.bits.zmm32
__ movl(rax, 0xE0);
__ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm
__ cmpl(rax, 0xE0);
__ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported
__ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset())));
__ movl(rax, Address(rsi, 0));
__ cmpl(rax, 0x50654); // If it is Skylake
__ jcc(Assembler::equal, legacy_setup);
// If UseAVX is unitialized or is set by the user to include EVEX
if (use_evex) {
// check _cpuid_info.sef_cpuid7_ebx.bits.avx512f
__ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset())));
__ movl(rax, 0x10000);
__ andl(rax, Address(rsi, 4)); // xcr0 bits sse | ymm
__ cmpl(rax, 0x10000);
__ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported
// check _cpuid_info.xem_xcr0_eax.bits.opmask
// check _cpuid_info.xem_xcr0_eax.bits.zmm512
// check _cpuid_info.xem_xcr0_eax.bits.zmm32
__ movl(rax, 0xE0);
__ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm
__ cmpl(rax, 0xE0);
__ jccb(Assembler::notEqual, legacy_setup); // jump if EVEX is not supported
if (FLAG_IS_DEFAULT(UseAVX)) {
__ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset())));
__ movl(rax, Address(rsi, 0));
__ cmpl(rax, 0x50654); // If it is Skylake
__ jcc(Assembler::equal, legacy_setup);
}
// EVEX setup: run in lowest evex mode
VM_Version::set_evex_cpuFeatures(); // Enable temporary to pass asserts
UseAVX = 3;
@ -455,27 +458,28 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
VM_Version::set_cpuinfo_cont_addr(__ pc());
// Returns here after signal. Save xmm0 to check it later.
// check _cpuid_info.sef_cpuid7_ebx.bits.avx512f
__ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset())));
__ movl(rax, 0x10000);
__ andl(rax, Address(rsi, 4));
__ cmpl(rax, 0x10000);
__ jcc(Assembler::notEqual, legacy_save_restore);
// check _cpuid_info.xem_xcr0_eax.bits.opmask
// check _cpuid_info.xem_xcr0_eax.bits.zmm512
// check _cpuid_info.xem_xcr0_eax.bits.zmm32
__ movl(rax, 0xE0);
__ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm
__ cmpl(rax, 0xE0);
__ jcc(Assembler::notEqual, legacy_save_restore);
__ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset())));
__ movl(rax, Address(rsi, 0));
__ cmpl(rax, 0x50654); // If it is Skylake
__ jcc(Assembler::equal, legacy_save_restore);
// If UseAVX is unitialized or is set by the user to include EVEX
if (use_evex) {
// check _cpuid_info.sef_cpuid7_ebx.bits.avx512f
__ lea(rsi, Address(rbp, in_bytes(VM_Version::sef_cpuid7_offset())));
__ movl(rax, 0x10000);
__ andl(rax, Address(rsi, 4));
__ cmpl(rax, 0x10000);
__ jcc(Assembler::notEqual, legacy_save_restore);
// check _cpuid_info.xem_xcr0_eax.bits.opmask
// check _cpuid_info.xem_xcr0_eax.bits.zmm512
// check _cpuid_info.xem_xcr0_eax.bits.zmm32
__ movl(rax, 0xE0);
__ andl(rax, Address(rbp, in_bytes(VM_Version::xem_xcr0_offset()))); // xcr0 bits sse | ymm
__ cmpl(rax, 0xE0);
__ jcc(Assembler::notEqual, legacy_save_restore);
if (FLAG_IS_DEFAULT(UseAVX)) {
__ lea(rsi, Address(rbp, in_bytes(VM_Version::std_cpuid1_offset())));
__ movl(rax, Address(rsi, 0));
__ cmpl(rax, 0x50654); // If it is Skylake
__ jcc(Assembler::equal, legacy_save_restore);
}
// EVEX check: run in lowest evex mode
VM_Version::set_evex_cpuFeatures(); // Enable temporary to pass asserts
UseAVX = 3;

View File

@ -3917,6 +3917,13 @@ operand eCXRegP(eRegP reg) %{
interface(REG_INTER);
%}
operand eDXRegP(eRegP reg) %{
constraint(ALLOC_IN_RC(edx_reg));
match(reg);
format %{ "EDX" %}
interface(REG_INTER);
%}
operand eSIRegP(eRegP reg) %{
constraint(ALLOC_IN_RC(esi_reg));
match(reg);
@ -8977,7 +8984,7 @@ instruct absI_rReg(rRegI dst, rRegI src, rRegI tmp, eFlagsReg cr)
%}
ins_pipe(ialu_reg_reg);
%}
%}
//----------Long Instructions------------------------------------------------
// Add Long Register with Register

View File

@ -267,6 +267,9 @@ reg_class ptr_rbx_reg(RBX, RBX_H);
// Singleton class for RSI pointer register
reg_class ptr_rsi_reg(RSI, RSI_H);
// Singleton class for RBP pointer register
reg_class ptr_rbp_reg(RBP, RBP_H);
// Singleton class for RDI pointer register
reg_class ptr_rdi_reg(RDI, RDI_H);
@ -3530,6 +3533,16 @@ operand rsi_RegP()
interface(REG_INTER);
%}
operand rbp_RegP()
%{
constraint(ALLOC_IN_RC(ptr_rbp_reg));
match(RegP);
match(rRegP);
format %{ %}
interface(REG_INTER);
%}
// Used in rep stosq
operand rdi_RegP()
%{

View File

@ -123,6 +123,7 @@ void ADLParser::parse() {
parse_err(SEMERR, "Did not declare 'register' definitions");
}
regBlock->addSpillRegClass();
regBlock->addDynamicRegClass();
// Done with parsing, check consistency.

View File

@ -245,12 +245,12 @@ void ArchDesc::inspectOperands() {
// Construct chain rules
build_chain_rule(op);
MatchRule &mrule = *op->_matrule;
Predicate *pred = op->_predicate;
MatchRule *mrule = op->_matrule;
Predicate *pred = op->_predicate;
// Grab the machine type of the operand
const char *rootOp = op->_ident;
mrule._machType = rootOp;
mrule->_machType = rootOp;
// Check for special cases
if (strcmp(rootOp,"Universe")==0) continue;
@ -271,10 +271,13 @@ void ArchDesc::inspectOperands() {
// Find result type for match.
const char *result = op->reduce_result();
bool has_root = false;
// Construct a MatchList for this entry
buildMatchList(op->_matrule, result, rootOp, pred, cost);
// Construct a MatchList for this entry.
// Iterate over the list to enumerate all match cases for operands with multiple match rules.
for (; mrule != NULL; mrule = mrule->_next) {
mrule->_machType = rootOp;
buildMatchList(mrule, result, rootOp, pred, cost);
}
}
}
@ -805,6 +808,8 @@ static const char *getRegMask(const char *reg_class_name) {
return "RegMask::Empty";
} else if (strcmp(reg_class_name,"stack_slots")==0) {
return "(Compile::current()->FIRST_STACK_mask())";
} else if (strcmp(reg_class_name, "dynamic")==0) {
return "*_opnds[0]->in_RegMask(0)";
} else {
char *rc_name = toUpper(reg_class_name);
const char *mask = "_mask";
@ -867,7 +872,7 @@ const char *ArchDesc::reg_mask(InstructForm &inForm) {
}
// Instructions producing 'Universe' use RegMask::Empty
if( strcmp(result,"Universe")==0 ) {
if (strcmp(result,"Universe") == 0) {
return "RegMask::Empty";
}

View File

@ -80,6 +80,15 @@ void RegisterForm::addSpillRegClass() {
_regClass.Insert(rc_name,reg_class);
}
// Called after parsing the Register block. Record the register class
// for operands which are overwritten after matching.
void RegisterForm::addDynamicRegClass() {
const char *rc_name = "dynamic";
RegClass* reg_class = new RegClass(rc_name);
reg_class->set_stack_version(false);
_rclasses.addName(rc_name);
_regClass.Insert(rc_name,reg_class);
}
// Provide iteration over all register definitions
// in the order used by the register allocator

View File

@ -104,6 +104,7 @@ public:
AllocClass *addAllocClass(char *allocName);
void addSpillRegClass();
void addDynamicRegClass();
// Provide iteration over all register definitions
// in the order used by the register allocator

View File

@ -2781,6 +2781,8 @@ static void defineIn_RegMask(FILE *fp, FormDict &globals, OperandForm &oper) {
// Return the sole RegMask.
if (strcmp(first_reg_class, "stack_slots") == 0) {
fprintf(fp," return &(Compile::current()->FIRST_STACK_mask());\n");
} else if (strcmp(first_reg_class, "dynamic") == 0) {
fprintf(fp," return &RegMask::Empty;\n");
} else {
const char* first_reg_class_to_upper = toUpper(first_reg_class);
fprintf(fp," return &%s_mask();\n", first_reg_class_to_upper);

View File

@ -32,9 +32,7 @@
#include "utilities/debug.hpp"
int BarrierSetNMethod::disarmed_value() const {
char* disarmed_addr = reinterpret_cast<char*>(Thread::current());
disarmed_addr += in_bytes(thread_disarmed_offset());
return *reinterpret_cast<int*>(disarmed_addr);
return *disarmed_value_address();
}
bool BarrierSetNMethod::supports_entry_barrier(nmethod* nm) {

View File

@ -34,13 +34,14 @@ class nmethod;
class BarrierSetNMethod: public CHeapObj<mtGC> {
bool supports_entry_barrier(nmethod* nm);
void deoptimize(nmethod* nm, address* return_addr_ptr);
int disarmed_value() const;
protected:
virtual int disarmed_value() const;
virtual bool nmethod_entry_barrier(nmethod* nm) = 0;
public:
virtual ByteSize thread_disarmed_offset() const = 0;
virtual int* disarmed_value_address() const = 0;
static int nmethod_stub_entry_barrier(address* return_address_ptr);
bool nmethod_osr_entry_barrier(nmethod* nm);

View File

@ -61,12 +61,10 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
return true;
}
int ZBarrierSetNMethod::disarmed_value() const {
// We override the default BarrierSetNMethod::disarmed_value() since
// this can be called by GC threads, which doesn't keep an up to date
// address_bad_mask.
const uintptr_t disarmed_addr = ((uintptr_t)&ZAddressBadMask) + ZNMethodDisarmedOffset;
return *((int*)disarmed_addr);
int* ZBarrierSetNMethod::disarmed_value_address() const {
const uintptr_t mask_addr = reinterpret_cast<uintptr_t>(&ZAddressBadMask);
const uintptr_t disarmed_addr = mask_addr + ZNMethodDisarmedOffset;
return reinterpret_cast<int*>(disarmed_addr);
}
ByteSize ZBarrierSetNMethod::thread_disarmed_offset() const {

View File

@ -31,11 +31,11 @@ class nmethod;
class ZBarrierSetNMethod : public BarrierSetNMethod {
protected:
virtual int disarmed_value() const;
virtual bool nmethod_entry_barrier(nmethod* nm);
public:
virtual ByteSize thread_disarmed_offset() const;
virtual int* disarmed_value_address() const;
};
#endif // SHARE_GC_Z_ZBARRIERSETNMETHOD_HPP

View File

@ -1385,14 +1385,6 @@ MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char*
}
DEBUG_ONLY(if (addr_delta == 0 && ArchiveRelocationMode == 1) {
// This is for simulating mmap failures at the requested address. We do it here (instead
// of MetaspaceShared::map_archives) so we can thoroughly test the code for failure handling
// (releasing all allocated resource, etc).
log_info(cds)("ArchiveRelocationMode == 1: always map archive(s) at an alternative address");
return MAP_ARCHIVE_MMAP_FAILURE;
});
header()->set_mapped_base_address(header()->requested_base_address() + addr_delta);
if (addr_delta != 0 && !relocate_pointers(addr_delta)) {
return MAP_ARCHIVE_OTHER_FAILURE;
@ -1446,12 +1438,14 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba
MemTracker::record_virtual_memory_type((address)requested_addr, mtClassShared);
}
if (MetaspaceShared::use_windows_memory_mapping() && addr_delta != 0) {
if (MetaspaceShared::use_windows_memory_mapping() && rs.is_reserved()) {
// This is the second time we try to map the archive(s). We have already created a ReservedSpace
// that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows
// can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the
// regions anyway, so there's no benefit for mmap anyway.
if (!read_region(i, requested_addr, size)) {
log_info(cds)("Failed to read %s shared space into reserved space at " INTPTR_FORMAT,
shared_region_name[i], p2i(requested_addr));
return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error.
}
} else {
@ -1459,7 +1453,8 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba
requested_addr, size, si->read_only(),
si->allow_exec());
if (base != requested_addr) {
log_info(cds)("Unable to map %s shared space at required address.", shared_region_name[i]);
log_info(cds)("Unable to map %s shared space at " INTPTR_FORMAT,
shared_region_name[i], p2i(requested_addr));
_memory_mapping_failed = true;
return MAP_ARCHIVE_MMAP_FAILURE;
}
@ -1468,7 +1463,7 @@ MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_ba
si->set_mapped_base(requested_addr);
if (!rs.is_reserved()) {
// When mapping on Windows with (addr_delta == 0), we don't reserve the address space for the regions
// When mapping on Windows for the first attempt, we don't reserve the address space for the regions
// (Windows can't mmap into a ReservedSpace). In this case, NMT requires we call it after
// os::map_memory has succeeded.
assert(MetaspaceShared::use_windows_memory_mapping(), "Windows memory mapping only");

View File

@ -2147,6 +2147,19 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
MapArchiveResult dynamic_result = (static_result == MAP_ARCHIVE_SUCCESS) ?
map_archive(dynamic_mapinfo, mapped_base_address, archive_space_rs) : MAP_ARCHIVE_OTHER_FAILURE;
DEBUG_ONLY(if (ArchiveRelocationMode == 1 && use_requested_addr) {
// This is for simulating mmap failures at the requested address. In debug builds, we do it
// here (after all archives have possibly been mapped), so we can thoroughly test the code for
// failure handling (releasing all allocated resource, etc).
log_info(cds)("ArchiveRelocationMode == 1: always map archive(s) at an alternative address");
if (static_result == MAP_ARCHIVE_SUCCESS) {
static_result = MAP_ARCHIVE_MMAP_FAILURE;
}
if (dynamic_result == MAP_ARCHIVE_SUCCESS) {
dynamic_result = MAP_ARCHIVE_MMAP_FAILURE;
}
});
if (static_result == MAP_ARCHIVE_SUCCESS) {
if (dynamic_result == MAP_ARCHIVE_SUCCESS) {
result = MAP_ARCHIVE_SUCCESS;
@ -2298,7 +2311,7 @@ static int dynamic_regions_count = 3;
MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs) {
assert(UseSharedSpaces, "must be runtime");
if (mapinfo == NULL) {
return MAP_ARCHIVE_SUCCESS; // no error has happeed -- trivially succeeded.
return MAP_ARCHIVE_SUCCESS; // The dynamic archive has not been specified. No error has happened -- trivially succeeded.
}
mapinfo->set_is_mapped(false);

View File

@ -55,8 +55,9 @@ void Parse::do_field_access(bool is_get, bool is_field) {
return;
}
// Deoptimize on putfield writes to call site target field.
if (!is_get && field->is_call_site_target()) {
// Deoptimize on putfield writes to call site target field outside of CallSite ctor.
if (!is_get && field->is_call_site_target() &&
!(method()->holder() == field_holder && method()->is_object_initializer())) {
uncommon_trap(Deoptimization::Reason_unhandled,
Deoptimization::Action_reinterpret,
NULL, "put to call site target field");

View File

@ -87,9 +87,10 @@ private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String nam
abstract
public class CallSite {
// The actual payload of this call site:
// The actual payload of this call site.
// Can be modified using {@link MethodHandleNatives#setCallSiteTargetNormal} or {@link MethodHandleNatives#setCallSiteTargetVolatile}.
/*package-private*/
MethodHandle target; // Note: This field is known to the JVM. Do not change.
final MethodHandle target; // Note: This field is known to the JVM.
/**
* Make a blank call site object with the given method type.
@ -129,11 +130,11 @@ public class CallSite {
*/
/*package-private*/
CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
this(targetType);
this(targetType); // need to initialize target to make CallSite.type() work in createTargetHook
ConstantCallSite selfCCS = (ConstantCallSite) this;
MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
checkTargetChange(this.target, boundTarget);
this.target = boundTarget;
setTargetNormal(boundTarget); // ConstantCallSite doesn't publish CallSite.target
UNSAFE.storeStoreFence(); // barrier between target and isFrozen updates
}
/**
@ -190,11 +191,12 @@ public class CallSite {
*/
public abstract void setTarget(MethodHandle newTarget);
void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
MethodType oldType = oldTarget.type();
private void checkTargetChange(MethodHandle newTarget) {
MethodType oldType = target.type(); // target is always present
MethodType newType = newTarget.type(); // null check!
if (!newType.equals(oldType))
if (newType != oldType) {
throw wrongTargetType(newTarget, oldType);
}
}
private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
@ -217,7 +219,7 @@ public class CallSite {
*/
public abstract MethodHandle dynamicInvoker();
/*non-public*/
/*package-private*/
MethodHandle makeDynamicInvoker() {
MethodHandle getTarget = getTargetHandle().bindArgumentL(0, this);
MethodHandle invoker = MethodHandles.exactInvoker(this.type());
@ -283,19 +285,24 @@ public class CallSite {
}
/*package-private*/
void setTargetNormal(MethodHandle newTarget) {
final void setTargetNormal(MethodHandle newTarget) {
checkTargetChange(newTarget);
MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
}
/*package-private*/
MethodHandle getTargetVolatile() {
final MethodHandle getTargetVolatile() {
return (MethodHandle) UNSAFE.getReferenceVolatile(this, getTargetOffset());
}
/*package-private*/
void setTargetVolatile(MethodHandle newTarget) {
final void setTargetVolatile(MethodHandle newTarget) {
checkTargetChange(newTarget);
MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
}
// this implements the upcall from the JVM, MethodHandleNatives.linkCallSite:
/*package-private*/
static CallSite makeSite(MethodHandle bootstrapMethod,
// Callee information:
String name, MethodType type,

View File

@ -25,6 +25,9 @@
package java.lang.invoke;
import jdk.internal.misc.Unsafe;
import jdk.internal.vm.annotation.Stable;
/**
* A {@code ConstantCallSite} is a {@link CallSite} whose target is permanent, and can never be changed.
* An {@code invokedynamic} instruction linked to a {@code ConstantCallSite} is permanently
@ -33,7 +36,10 @@ package java.lang.invoke;
* @since 1.7
*/
public class ConstantCallSite extends CallSite {
private final boolean isFrozen;
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
@Stable // should NOT be constant folded during instance initialization (isFrozen == false)
/*final*/ private boolean isFrozen;
/**
* Creates a call site with a permanent target.
@ -43,6 +49,7 @@ public class ConstantCallSite extends CallSite {
public ConstantCallSite(MethodHandle target) {
super(target);
isFrozen = true;
UNSAFE.storeStoreFence(); // properly publish isFrozen update
}
/**
@ -79,8 +86,9 @@ public class ConstantCallSite extends CallSite {
* @throws Throwable anything else thrown by the hook function
*/
protected ConstantCallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
super(targetType, createTargetHook);
super(targetType, createTargetHook); // "this" instance leaks into createTargetHook
isFrozen = true;
UNSAFE.storeStoreFence(); // properly publish isFrozen
}
/**

View File

@ -152,7 +152,6 @@ public class MutableCallSite extends CallSite {
* @see #getTarget
*/
@Override public void setTarget(MethodHandle newTarget) {
checkTargetChange(this.target, newTarget);
setTargetNormal(newTarget);
}

View File

@ -96,7 +96,6 @@ public class VolatileCallSite extends CallSite {
* @see #getTarget
*/
@Override public void setTarget(MethodHandle newTarget) {
checkTargetChange(getTargetVolatile(), newTarget);
setTargetVolatile(newTarget);
}

View File

@ -54,6 +54,8 @@ public @interface PreviewFeature {
public boolean essentialAPI() default false;
public enum Feature {
TEXT_BLOCKS;
PATTERN_MATCHING_IN_INSTANCEOF,
TEXT_BLOCKS,
;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.source.tree;
import javax.lang.model.element.Name;
/**
* {@preview Associated with pattern matching for instanceof, a preview feature of
* the Java language.
*
* This interface is associated with <i>pattern matching for instanceof</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* A binding pattern tree
*
* @since 14
*/
public interface BindingPatternTree extends PatternTree {
/**
* Returns the type of the bind variable.
* @return the type
*/
Tree getType();
/**
* A binding variable name.
* @return the name of the binding variable
*/
Name getBinding();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2019, 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
@ -51,4 +51,33 @@ public interface InstanceOfTree extends ExpressionTree {
* @return the type
*/
Tree getType();
/**
* {@preview Associated with pattern matching for instanceof, a preview feature of
* the Java language.
*
* This method is associated with <i>pattern matching for instanceof</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Returns the tested pattern, or null if this instanceof does not use
* a pattern.
*
* <p>For instanceof with a pattern, i.e. in the following form:
* <pre>
* <em>expression</em> instanceof <em>type</em> <em>variable name</em>
* </pre>
* returns the pattern.
*
* <p>For instanceof without a pattern, i.e. in the following form:
* <pre>
* <em>expression</em> instanceof <em>type</em>
* </pre>
* returns null.
*
* @return the tested pattern, or null if this instanceof does not use a pattern.
* @since 14
*/
PatternTree getPattern();
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.source.tree;
/**
* {@preview Associated with pattern matching for instanceof, a preview feature of
* the Java language.
*
* This interface is associated with <i>pattern matching for instanceof</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* A tree node used as the base class for the different kinds of
* statements.
*
* @since 14
*/
public interface PatternTree extends Tree {}

View File

@ -219,6 +219,21 @@ public interface Tree {
*/
PARENTHESIZED(ParenthesizedTree.class),
/**
* {@preview Associated with pattern matching for instanceof, a preview feature of
* the Java language.
*
* This enum constant is associated with <i>pattern matching for instanceof</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Used for instances of {@link BindingPatternTree}.
*
* @since 14
*/
BINDING_PATTERN(BindingPatternTree.class),
/**
* Used for instances of {@link PrimitiveTypeTree}.
*/

View File

@ -257,6 +257,23 @@ public interface TreeVisitor<R,P> {
*/
R visitLiteral(LiteralTree node, P p);
/**
* {@preview Associated with pattern matching for instanceof, a preview feature of
* the Java language.
*
* This method is associated with <i>pattern matching for instanceof</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Visits an BindingPattern node.
* @param node the node being visited
* @param p a parameter value
* @return a result value
* @since 14
*/
R visitBindingPattern(BindingPatternTree node, P p);
/**
* Visits a MethodTree node.
* @param node the node being visited

View File

@ -551,6 +551,19 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
return defaultAction(node, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
* @since 14
*/
@Override
public R visitBindingPattern(BindingPatternTree node, P p) {
return defaultAction(node, p);
}
/**
* {@inheritDoc} This implementation calls {@code defaultAction}.
*

View File

@ -667,10 +667,27 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
@Override
public R visitInstanceOf(InstanceOfTree node, P p) {
R r = scan(node.getExpression(), p);
r = scanAndReduce(node.getType(), p, r);
if (node.getPattern() != null) {
r = scanAndReduce(node.getPattern(), p, r);
} else {
r = scanAndReduce(node.getType(), p, r);
}
return r;
}
/**
* {@inheritDoc} This implementation scans the children in left to right order.
*
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
* @since 14
*/
@Override
public R visitBindingPattern(BindingPatternTree node, P p) {
return scan(node.getType(), p);
}
/**
* {@inheritDoc} This implementation scans the children in left to right order.
*

View File

@ -334,6 +334,16 @@ public class Flags {
*/
public static final long PREVIEW_ESSENTIAL_API = 1L<<58; //any Symbol kind
/**
* Flag to indicate the given variable is a match binding variable.
*/
public static final long MATCH_BINDING = 1L<<59;
/**
* A flag to indicate a match binding variable whose scope extends after the current statement.
*/
public static final long MATCH_BINDING_TO_OUTER = 1L<<60;
/** Modifier masks.
*/
public static final int
@ -453,7 +463,9 @@ public class Flags {
ANONCONSTR_BASED(Flags.ANONCONSTR_BASED),
NAME_FILLED(Flags.NAME_FILLED),
PREVIEW_API(Flags.PREVIEW_API),
PREVIEW_ESSENTIAL_API(Flags.PREVIEW_ESSENTIAL_API);
PREVIEW_ESSENTIAL_API(Flags.PREVIEW_ESSENTIAL_API),
MATCH_BINDING(Flags.MATCH_BINDING),
MATCH_BINDING_TO_OUTER(Flags.MATCH_BINDING_TO_OUTER);
Flag(long flag) {
this.value = flag;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, 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
@ -165,7 +165,9 @@ public class Preview {
* @return true, if given feature is a preview feature.
*/
public boolean isPreview(Feature feature) {
if (feature == Feature.TEXT_BLOCKS)
if (feature == Feature.PATTERN_MATCHING_IN_INSTANCEOF ||
feature == Feature.REIFIABLE_TYPES_INSTANCEOF ||
feature == Feature.TEXT_BLOCKS)
return true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'

View File

@ -198,7 +198,10 @@ public enum Source {
SWITCH_MULTIPLE_CASE_LABELS(JDK14, Fragments.FeatureMultipleCaseLabels, DiagKind.PLURAL),
SWITCH_RULE(JDK14, Fragments.FeatureSwitchRules, DiagKind.PLURAL),
SWITCH_EXPRESSION(JDK14, Fragments.FeatureSwitchExpressions, DiagKind.PLURAL),
TEXT_BLOCKS(JDK14, Fragments.FeatureTextBlocks, DiagKind.PLURAL);
TEXT_BLOCKS(JDK14, Fragments.FeatureTextBlocks, DiagKind.PLURAL),
PATTERN_MATCHING_IN_INSTANCEOF(JDK14, Fragments.FeaturePatternMatchingInstanceof, DiagKind.NORMAL),
REIFIABLE_TYPES_INSTANCEOF(JDK14, Fragments.FeatureReifiableTypesInstanceof, DiagKind.PLURAL),
;
enum DiagKind {
NORMAL,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2009, 2019, 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
@ -390,22 +390,26 @@ public class TypeAnnotations {
sym.getKind() == ElementKind.LOCAL_VARIABLE ||
sym.getKind() == ElementKind.RESOURCE_VARIABLE ||
sym.getKind() == ElementKind.EXCEPTION_PARAMETER) {
// Make sure all type annotations from the symbol are also
// on the owner. If the owner is an initializer block, propagate
// to the type.
final long ownerFlags = sym.owner.flags();
if ((ownerFlags & Flags.BLOCK) != 0) {
// Store init and clinit type annotations with the ClassSymbol
// to allow output in Gen.normalizeDefs.
ClassSymbol cs = (ClassSymbol) sym.owner.owner;
if ((ownerFlags & Flags.STATIC) != 0) {
cs.appendClassInitTypeAttributes(typeAnnotations);
} else {
cs.appendInitTypeAttributes(typeAnnotations);
}
appendTypeAnnotationsToOwner(sym, typeAnnotations);
}
}
private void appendTypeAnnotationsToOwner(Symbol sym, List<Attribute.TypeCompound> typeAnnotations) {
// Make sure all type annotations from the symbol are also
// on the owner. If the owner is an initializer block, propagate
// to the type.
final long ownerFlags = sym.owner.flags();
if ((ownerFlags & Flags.BLOCK) != 0) {
// Store init and clinit type annotations with the ClassSymbol
// to allow output in Gen.normalizeDefs.
ClassSymbol cs = (ClassSymbol) sym.owner.owner;
if ((ownerFlags & Flags.STATIC) != 0) {
cs.appendClassInitTypeAttributes(typeAnnotations);
} else {
sym.owner.appendUniqueTypeAttributes(sym.getRawTypeAttributes());
cs.appendInitTypeAttributes(typeAnnotations);
}
} else {
sym.owner.appendUniqueTypeAttributes(typeAnnotations);
}
}
@ -943,10 +947,11 @@ public class TypeAnnotations {
" within frame " + frame);
}
case BINDING_PATTERN:
case VARIABLE:
VarSymbol v = ((JCVariableDecl)frame).sym;
VarSymbol v = frame.hasTag(Tag.BINDINGPATTERN) ? ((JCBindingPattern) frame).symbol : ((JCVariableDecl) frame).sym;
if (v.getKind() != ElementKind.FIELD) {
v.owner.appendUniqueTypeAttributes(v.getRawTypeAttributes());
appendTypeAnnotationsToOwner(v, v.getRawTypeAttributes());
}
switch (v.getKind()) {
case LOCAL_VARIABLE:

View File

@ -1121,6 +1121,13 @@ public class Annotate {
}
}
@Override
public void visitBindingPattern(JCTree.JCBindingPattern tree) {
//type binding pattern's type will be annotated separatelly, avoid
//adding its annotations into the owning method here (would clash
//with repeatable annotations).
}
@Override
public void visitClassDef(JCClassDecl tree) {
// We can only hit a classdef if it is declared within

View File

@ -49,6 +49,7 @@ import com.sun.tools.javac.code.Types.FunctionDescriptorLookupError;
import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext;
import com.sun.tools.javac.comp.Check.CheckContext;
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
import com.sun.tools.javac.jvm.*;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.Diamond;
import static com.sun.tools.javac.resources.CompilerProperties.Fragments.DiamondInvalidArg;
@ -110,6 +111,7 @@ public class Attr extends JCTree.Visitor {
final Enter enter;
final Target target;
final Types types;
final Preview preview;
final JCDiagnostic.Factory diags;
final TypeAnnotations typeAnnotations;
final DeferredLintHandler deferredLintHandler;
@ -117,6 +119,7 @@ public class Attr extends JCTree.Visitor {
final Dependencies dependencies;
final Annotate annotate;
final ArgumentAttr argumentAttr;
final MatchBindingsComputer matchBindingsComputer;
public static Attr instance(Context context) {
Attr instance = context.get(attrKey);
@ -145,6 +148,7 @@ public class Attr extends JCTree.Visitor {
cfolder = ConstFold.instance(context);
target = Target.instance(context);
types = Types.instance(context);
preview = Preview.instance(context);
diags = JCDiagnostic.Factory.instance(context);
annotate = Annotate.instance(context);
typeAnnotations = TypeAnnotations.instance(context);
@ -152,6 +156,7 @@ public class Attr extends JCTree.Visitor {
typeEnvs = TypeEnvs.instance(context);
dependencies = Dependencies.instance(context);
argumentAttr = ArgumentAttr.instance(context);
matchBindingsComputer = MatchBindingsComputer.instance(context);
Options options = Options.instance(context);
@ -161,6 +166,9 @@ public class Attr extends JCTree.Visitor {
allowLambda = Feature.LAMBDA.allowedInSource(source);
allowDefaultMethods = Feature.DEFAULT_METHODS.allowedInSource(source);
allowStaticInterfaceMethods = Feature.STATIC_INTERFACE_METHODS.allowedInSource(source);
allowReifiableTypesInInstanceof =
Feature.REIFIABLE_TYPES_INSTANCEOF.allowedInSource(source) &&
(!preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF) || preview.isEnabled());
sourceName = source.name;
useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning");
@ -193,6 +201,10 @@ public class Attr extends JCTree.Visitor {
*/
boolean allowStaticInterfaceMethods;
/** Switch: reifiable types in instanceof enabled?
*/
boolean allowReifiableTypesInInstanceof;
/**
* Switch: warn about use of variable before declaration?
* RFE: 6425594
@ -292,6 +304,8 @@ public class Attr extends JCTree.Visitor {
isAssignableAsBlankFinal(v, env)))) {
if (v.isResourceVariable()) { //TWR resource
log.error(pos, Errors.TryResourceMayNotBeAssigned(v));
} else if ((v.flags() & MATCH_BINDING) != 0) {
log.error(pos, Errors.PatternBindingMayNotBeAssigned(v));
} else {
log.error(pos, Errors.CantAssignValToFinalVar(v));
}
@ -1298,29 +1312,73 @@ public class Attr extends JCTree.Visitor {
public void visitDoLoop(JCDoWhileLoop tree) {
attribStat(tree.body, env.dup(tree));
attribExpr(tree.cond, env, syms.booleanType);
if (!breaksOutOf(tree, tree.body)) {
//include condition's body when false after the while, if cannot get out of the loop
List<BindingSymbol> bindings = matchBindingsComputer.getMatchBindings(tree.cond, false);
bindings.forEach(env.info.scope::enter);
bindings.forEach(BindingSymbol::preserveBinding);
}
result = null;
}
public void visitWhileLoop(JCWhileLoop tree) {
attribExpr(tree.cond, env, syms.booleanType);
attribStat(tree.body, env.dup(tree));
// include condition's bindings when true in the body:
Env<AttrContext> whileEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
try {
attribStat(tree.body, whileEnv.dup(tree));
} finally {
whileEnv.info.scope.leave();
}
if (!breaksOutOf(tree, tree.body)) {
//include condition's bindings when false after the while, if cannot get out of the loop
List<BindingSymbol> bindings =
matchBindingsComputer.getMatchBindings(tree.cond, false);
bindings.forEach(env.info.scope::enter);
bindings.forEach(BindingSymbol::preserveBinding);
}
result = null;
}
private boolean breaksOutOf(JCTree loop, JCTree body) {
preFlow(body);
return flow.breaksOutOf(env, loop, body, make);
}
public void visitForLoop(JCForLoop tree) {
Env<AttrContext> loopEnv =
env.dup(env.tree, env.info.dup(env.info.scope.dup()));
try {
attribStats(tree.init, loopEnv);
if (tree.cond != null) attribExpr(tree.cond, loopEnv, syms.booleanType);
loopEnv.tree = tree; // before, we were not in loop!
attribStats(tree.step, loopEnv);
attribStat(tree.body, loopEnv);
List<BindingSymbol> matchBindings = List.nil();
if (tree.cond != null) {
attribExpr(tree.cond, loopEnv, syms.booleanType);
// include condition's bindings when true in the body and step:
matchBindings = matchBindingsComputer.getMatchBindings(tree.cond, true);
}
Env<AttrContext> bodyEnv = bindingEnv(loopEnv, matchBindings);
try {
bodyEnv.tree = tree; // before, we were not in loop!
attribStats(tree.step, bodyEnv);
attribStat(tree.body, bodyEnv);
} finally {
bodyEnv.info.scope.leave();
}
result = null;
}
finally {
loopEnv.info.scope.leave();
}
if (!breaksOutOf(tree, tree.body)) {
//include condition's body when false after the while, if cannot get out of the loop
List<BindingSymbol> bindings =
matchBindingsComputer.getMatchBindings(tree.cond, false);
bindings.forEach(env.info.scope::enter);
bindings.forEach(BindingSymbol::preserveBinding);
}
}
public void visitForeachLoop(JCEnhancedForLoop tree) {
@ -1673,8 +1731,26 @@ public class Attr extends JCTree.Visitor {
unknownExprInfo :
resultInfo.dup(conditionalContext(resultInfo.checkContext));
Type truetype = attribTree(tree.truepart, env, condInfo);
Type falsetype = attribTree(tree.falsepart, env, condInfo);
// x ? y : z
// include x's bindings when true in y
// include x's bindings when false in z
Type truetype;
Env<AttrContext> trueEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, true));
try {
truetype = attribTree(tree.truepart, trueEnv, condInfo);
} finally {
trueEnv.info.scope.leave();
}
Type falsetype;
Env<AttrContext> falseEnv = bindingEnv(env, matchBindingsComputer.getMatchBindings(tree.cond, false));
try {
falsetype = attribTree(tree.falsepart, falseEnv, condInfo);
} finally {
falseEnv.info.scope.leave();
}
Type owntype = (tree.polyKind == PolyKind.STANDALONE) ?
condType(List.of(tree.truepart.pos(), tree.falsepart.pos()),
@ -1829,15 +1905,77 @@ public class Attr extends JCTree.Visitor {
BOOLEAN,
};
Env<AttrContext> bindingEnv(Env<AttrContext> env, List<BindingSymbol> bindings) {
Env<AttrContext> env1 = env.dup(env.tree, env.info.dup(env.info.scope.dup()));
bindings.forEach(env1.info.scope::enter);
return env1;
}
public void visitIf(JCIf tree) {
attribExpr(tree.cond, env, syms.booleanType);
attribStat(tree.thenpart, env);
if (tree.elsepart != null)
attribStat(tree.elsepart, env);
// if (x) { y } [ else z ]
// include x's bindings when true in y
// include x's bindings when false in z
List<BindingSymbol> thenBindings = matchBindingsComputer.getMatchBindings(tree.cond, true);
Env<AttrContext> thenEnv = bindingEnv(env, thenBindings);
try {
attribStat(tree.thenpart, thenEnv);
} finally {
thenEnv.info.scope.leave();
}
preFlow(tree.thenpart);
boolean aliveAfterThen = flow.aliveAfter(env, tree.thenpart, make);
boolean aliveAfterElse;
List<BindingSymbol> elseBindings = matchBindingsComputer.getMatchBindings(tree.cond, false);
if (tree.elsepart != null) {
Env<AttrContext> elseEnv = bindingEnv(env, elseBindings);
try {
attribStat(tree.elsepart, elseEnv);
} finally {
elseEnv.info.scope.leave();
}
preFlow(tree.elsepart);
aliveAfterElse = flow.aliveAfter(env, tree.elsepart, make);
} else {
aliveAfterElse = true;
}
chk.checkEmptyIf(tree);
List<BindingSymbol> afterIfBindings = List.nil();
if (aliveAfterThen && !aliveAfterElse) {
afterIfBindings = thenBindings;
} else if (aliveAfterElse && !aliveAfterThen) {
afterIfBindings = elseBindings;
}
afterIfBindings.forEach(env.info.scope::enter);
afterIfBindings.forEach(BindingSymbol::preserveBinding);
result = null;
}
void preFlow(JCTree tree) {
new PostAttrAnalyzer() {
@Override
public void scan(JCTree tree) {
if (tree == null ||
(tree.type != null &&
tree.type == Type.stuckType)) {
//don't touch stuck expressions!
return;
}
super.scan(tree);
}
}.scan(tree);
}
public void visitExec(JCExpressionStatement tree) {
//a fresh environment is required for 292 inference to work properly ---
//see Infer.instantiatePolymorphicSignatureInstance()
@ -3521,7 +3659,32 @@ public class Attr extends JCTree.Visitor {
public void visitBinary(JCBinary tree) {
// Attribute arguments.
Type left = chk.checkNonVoid(tree.lhs.pos(), attribExpr(tree.lhs, env));
Type right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, env));
// x && y
// include x's bindings when true in y
// x || y
// include x's bindings when false in y
List<BindingSymbol> matchBindings;
switch (tree.getTag()) {
case AND:
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
break;
case OR:
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false);
break;
default:
matchBindings = List.nil();
break;
}
Env<AttrContext> rhsEnv = bindingEnv(env, matchBindings);
Type right;
try {
right = chk.checkNonVoid(tree.rhs.pos(), attribExpr(tree.rhs, rhsEnv));
} finally {
rhsEnv.info.scope.leave();
}
// Find operator.
Symbol operator = tree.operator = operators.resolveBinary(tree, tree.getTag(), left, right);
Type owntype = types.createErrorType(tree.type);
@ -3587,19 +3750,63 @@ public class Attr extends JCTree.Visitor {
public void visitTypeTest(JCInstanceOf tree) {
Type exprtype = chk.checkNullOrRefType(
tree.expr.pos(), attribExpr(tree.expr, env));
Type clazztype = attribType(tree.clazz, env);
Type clazztype;
JCTree typeTree;
if (tree.pattern.getTag() == BINDINGPATTERN) {
attribTree(tree.pattern, env, unknownExprInfo);
clazztype = tree.pattern.type;
JCBindingPattern pattern = (JCBindingPattern) tree.pattern;
typeTree = pattern.vartype;
if (!clazztype.hasTag(TYPEVAR)) {
clazztype = chk.checkClassOrArrayType(pattern.vartype.pos(), clazztype);
}
} else {
clazztype = attribType(tree.pattern, env);
typeTree = tree.pattern;
}
if (!clazztype.hasTag(TYPEVAR)) {
clazztype = chk.checkClassOrArrayType(tree.clazz.pos(), clazztype);
clazztype = chk.checkClassOrArrayType(typeTree.pos(), clazztype);
}
if (!clazztype.isErroneous() && !types.isReifiable(clazztype)) {
log.error(tree.clazz.pos(), Errors.IllegalGenericTypeForInstof);
clazztype = types.createErrorType(clazztype);
boolean valid = false;
if (allowReifiableTypesInInstanceof) {
if (preview.isPreview(Feature.REIFIABLE_TYPES_INSTANCEOF)) {
preview.warnPreview(tree.expr.pos(), Feature.REIFIABLE_TYPES_INSTANCEOF);
}
Warner warner = new Warner();
if (!types.isCastable(exprtype, clazztype, warner)) {
chk.basicHandler.report(tree.expr.pos(),
diags.fragment(Fragments.InconvertibleTypes(exprtype, clazztype)));
} else if (warner.hasLint(LintCategory.UNCHECKED)) {
log.error(tree.expr.pos(),
Errors.InstanceofReifiableNotSafe(exprtype, clazztype));
} else {
valid = true;
}
} else {
log.error(typeTree.pos(), Errors.IllegalGenericTypeForInstof);
}
if (!valid) {
clazztype = types.createErrorType(clazztype);
}
}
chk.validate(tree.clazz, env, false);
chk.validate(typeTree, env, false);
chk.checkCastable(tree.expr.pos(), exprtype, clazztype);
result = check(tree, syms.booleanType, KindSelector.VAL, resultInfo);
}
public void visitBindingPattern(JCBindingPattern tree) {
ResultInfo varInfo = new ResultInfo(KindSelector.TYP, resultInfo.pt, resultInfo.checkContext);
tree.type = attribTree(tree.vartype, env, varInfo);
VarSymbol v = tree.symbol = new BindingSymbol(tree.name, tree.vartype.type, env.info.scope.owner);
if (chk.checkUnique(tree.pos(), v, env.info.scope)) {
chk.checkTransparentVar(tree.pos(), v, env.info.scope);
}
annotate.queueScanTreeAndTypeAnnotate(tree.vartype, env, v, tree.pos());
annotate.flush();
result = tree.type;
}
public void visitIndexed(JCArrayAccess tree) {
Type owntype = types.createErrorType(tree.type);
Type atype = attribExpr(tree.indexed, env);
@ -4991,8 +5198,8 @@ public class Attr extends JCTree.Visitor {
super.visitTypeCast(tree);
}
public void visitTypeTest(JCInstanceOf tree) {
if (tree.clazz != null && tree.clazz.type != null)
validateAnnotatedType(tree.clazz, tree.clazz.type);
if (tree.pattern != null && !(tree.pattern instanceof JCPattern) && tree.pattern.type != null)
validateAnnotatedType(tree.pattern, tree.pattern.type);
super.visitTypeTest(tree);
}
public void visitNewClass(JCNewClass tree) {
@ -5252,6 +5459,15 @@ public class Attr extends JCTree.Visitor {
super.visitVarDef(that);
}
@Override
public void visitBindingPattern(JCBindingPattern that) {
if (that.symbol == null) {
that.symbol = new BindingSymbol(that.name, that.type, syms.noSymbol);
that.symbol.adr = 0;
}
super.visitBindingPattern(that);
}
@Override
public void visitNewClass(JCNewClass that) {
if (that.constructor == null) {

View File

@ -3486,6 +3486,11 @@ public class Check {
duplicateErasureError(pos, sym, byName);
sym.flags_field |= CLASH;
return true;
} else if ((sym.flags() & MATCH_BINDING) != 0 &&
(byName.flags() & MATCH_BINDING) != 0 &&
(byName.flags() & MATCH_BINDING_TO_OUTER) == 0) {
//this error will be reported separatelly in MatchBindingsComputer
return false;
} else {
duplicateError(pos, byName);
return false;

View File

@ -59,9 +59,10 @@ public class CompileStates extends HashMap<Env<AttrContext>, CompileStates.Compi
ATTR(4),
FLOW(5),
TRANSTYPES(6),
UNLAMBDA(7),
LOWER(8),
GENERATE(9);
TRANSPATTERNS(7),
UNLAMBDA(8),
LOWER(9),
GENERATE(10);
CompileState(int value) {
this.value = value;

View File

@ -255,6 +255,41 @@ public class Flow {
}
}
public boolean aliveAfter(Env<AttrContext> env, JCTree that, TreeMaker make) {
//we need to disable diagnostics temporarily; the problem is that if
//"that" contains e.g. an unreachable statement, an error
//message will be reported and will cause compilation to skip the flow analyis
//step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
//related errors, which will allow for more errors to be detected
Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
try {
SnippetAliveAnalyzer analyzer = new SnippetAliveAnalyzer();
analyzer.analyzeTree(env, that, make);
return analyzer.isAlive();
} finally {
log.popDiagnosticHandler(diagHandler);
}
}
public boolean breaksOutOf(Env<AttrContext> env, JCTree loop, JCTree body, TreeMaker make) {
//we need to disable diagnostics temporarily; the problem is that if
//"that" contains e.g. an unreachable statement, an error
//message will be reported and will cause compilation to skip the flow analyis
//step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
//related errors, which will allow for more errors to be detected
Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
try {
boolean[] breaksOut = new boolean[1];
SnippetBreakAnalyzer analyzer = new SnippetBreakAnalyzer(loop);
analyzer.analyzeTree(env, body, make);
return analyzer.breaksOut();
} finally {
log.popDiagnosticHandler(diagHandler);
}
}
/**
* Definite assignment scan mode
*/
@ -1466,6 +1501,38 @@ public class Flow {
}
}
/**
* Determine if alive after the given tree.
*/
class SnippetAliveAnalyzer extends AliveAnalyzer {
@Override
public void visitClassDef(JCClassDecl tree) {
//skip
}
public boolean isAlive() {
return super.alive != Liveness.DEAD;
}
}
class SnippetBreakAnalyzer extends AliveAnalyzer {
private final JCTree loop;
private boolean breaksOut;
public SnippetBreakAnalyzer(JCTree loop) {
this.loop = loop;
}
@Override
public void visitBreak(JCBreak tree) {
breaksOut |= (super.alive == Liveness.ALIVE && tree.target == loop);
super.visitBreak(tree);
}
public boolean breaksOut() {
return breaksOut;
}
}
/**
* Specialized pass that performs DA/DU on a lambda
*/

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.comp;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.resources.CompilerProperties.Errors;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCUnary;
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
import com.sun.tools.javac.tree.TreeScanner;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
public class MatchBindingsComputer extends TreeScanner {
protected static final Context.Key<MatchBindingsComputer> matchBindingsComputerKey = new Context.Key<>();
private final Log log;
private final Types types;
boolean whenTrue;
List<BindingSymbol> bindings;
public static MatchBindingsComputer instance(Context context) {
MatchBindingsComputer instance = context.get(matchBindingsComputerKey);
if (instance == null)
instance = new MatchBindingsComputer(context);
return instance;
}
protected MatchBindingsComputer(Context context) {
this.log = Log.instance(context);
this.types = Types.instance(context);
}
public List<BindingSymbol> getMatchBindings(JCTree expression, boolean whenTrue) {
this.whenTrue = whenTrue;
this.bindings = List.nil();
scan(expression);
return bindings;
}
@Override
public void visitBindingPattern(JCBindingPattern tree) {
bindings = whenTrue ? List.of(tree.symbol) : List.nil();
}
@Override
public void visitBinary(JCBinary tree) {
switch (tree.getTag()) {
case AND:
// e.T = union(x.T, y.T)
// e.F = intersection(x.F, y.F)
scan(tree.lhs);
List<BindingSymbol> lhsBindings = bindings;
scan(tree.rhs);
List<BindingSymbol> rhsBindings = bindings;
bindings = whenTrue ? union(tree, lhsBindings, rhsBindings) : intersection(tree, lhsBindings, rhsBindings);
break;
case OR:
// e.T = intersection(x.T, y.T)
// e.F = union(x.F, y.F)
scan(tree.lhs);
lhsBindings = bindings;
scan(tree.rhs);
rhsBindings = bindings;
bindings = whenTrue ? intersection(tree, lhsBindings, rhsBindings) : union(tree, lhsBindings, rhsBindings);
break;
default:
super.visitBinary(tree);
break;
}
}
@Override
public void visitUnary(JCUnary tree) {
switch (tree.getTag()) {
case NOT:
// e.T = x.F // flip 'em
// e.F = x.T
whenTrue = !whenTrue;
scan(tree.arg);
whenTrue = !whenTrue;
break;
default:
super.visitUnary(tree);
break;
}
}
@Override
public void visitConditional(JCConditional tree) {
/* if e = "x ? y : z", then:
e.T = union(intersect(y.T, z.T), intersect(x.T, z.T), intersect(x.F, y.T))
e.F = union(intersect(y.F, z.F), intersect(x.T, z.F), intersect(x.F, y.F))
*/
if (whenTrue) {
List<BindingSymbol> xT, yT, zT, xF;
scan(tree.cond);
xT = bindings;
scan(tree.truepart);
yT = bindings;
scan(tree.falsepart);
zT = bindings;
whenTrue = false;
scan(tree.cond);
xF = bindings;
whenTrue = true;
bindings = union(tree, intersection(tree, yT, zT), intersection(tree, xT, zT), intersection(tree, xF, yT));
} else {
List<BindingSymbol> xF, yF, zF, xT;
scan(tree.cond);
xF = bindings;
scan(tree.truepart);
yF = bindings;
scan(tree.falsepart);
zF = bindings;
whenTrue = true;
scan(tree.cond);
xT = bindings;
whenTrue = false;
bindings = union(tree, intersection(tree, yF, zF), intersection(tree, xT, zF), intersection(tree, xF, yF));
}
}
private List<BindingSymbol> intersection(JCTree tree, List<BindingSymbol> lhsBindings, List<BindingSymbol> rhsBindings) {
// It is an error if, for intersection(a,b), if a and b contain the same variable name (may be eventually relaxed to merge variables of same type)
List<BindingSymbol> list = List.nil();
for (BindingSymbol v1 : lhsBindings) {
for (BindingSymbol v2 : rhsBindings) {
if (v1.name == v2.name) {
log.error(tree.pos(), Errors.MatchBindingExists);
list = list.append(v2);
}
}
}
return list;
}
@SafeVarargs
private final List<BindingSymbol> union(JCTree tree, List<BindingSymbol> lhsBindings, List<BindingSymbol> ... rhsBindings_s) {
// It is an error if for union(a,b), a and b contain the same name (disjoint union).
List<BindingSymbol> list = lhsBindings;
for (List<BindingSymbol> rhsBindings : rhsBindings_s) {
for (BindingSymbol v : rhsBindings) {
for (BindingSymbol ov : list) {
if (ov.name == v.name) {
log.error(tree.pos(), Errors.MatchBindingExists);
}
}
list = list.append(v);
}
}
return list;
}
@Override
public void scan(JCTree tree) {
bindings = List.nil();
super.scan(tree);
}
public static class BindingSymbol extends VarSymbol {
public BindingSymbol(Name name, Type type, Symbol owner) {
super(Flags.FINAL | Flags.HASINIT | Flags.MATCH_BINDING, name, type, owner);
}
public boolean isAliasFor(BindingSymbol b) {
return aliases().containsAll(b.aliases());
}
List<BindingSymbol> aliases() {
return List.of(this);
}
public void preserveBinding() {
flags_field |= Flags.MATCH_BINDING_TO_OUTER;
}
public boolean isPreserved() {
return (flags_field & Flags.MATCH_BINDING_TO_OUTER) != 0;
}
}
}

View File

@ -0,0 +1,457 @@
/*
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.comp;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.code.Types;
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCConditional;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCForLoop;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCIf;
import com.sun.tools.javac.tree.JCTree.JCInstanceOf;
import com.sun.tools.javac.tree.JCTree.JCLabeledStatement;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
import com.sun.tools.javac.tree.JCTree.JCWhileLoop;
import com.sun.tools.javac.tree.JCTree.Tag;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.Assert;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.Options;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import static com.sun.tools.javac.code.TypeTag.BOOLEAN;
import static com.sun.tools.javac.code.TypeTag.BOT;
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
import com.sun.tools.javac.jvm.Target;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.LetExpr;
import com.sun.tools.javac.util.List;
/**
* This pass translates pattern-matching constructs, such as instanceof <pattern>.
*/
public class TransPatterns extends TreeTranslator {
protected static final Context.Key<TransPatterns> transPatternsKey = new Context.Key<>();
public static TransPatterns instance(Context context) {
TransPatterns instance = context.get(transPatternsKey);
if (instance == null)
instance = new TransPatterns(context);
return instance;
}
private final Symtab syms;
private final Types types;
private final Operators operators;
private final Log log;
private final ConstFold constFold;
private final Names names;
private final Target target;
private final MatchBindingsComputer matchBindingsComputer;
private TreeMaker make;
BindingContext bindingContext = new BindingContext() {
@Override
VarSymbol getBindingFor(BindingSymbol varSymbol) {
return null;
}
@Override
JCStatement decorateStatement(JCStatement stat) {
return stat;
}
@Override
JCExpression decorateExpression(JCExpression expr) {
return expr;
}
@Override
BindingContext pop() {
//do nothing
return this;
}
@Override
boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
return false;
}
};
JCLabeledStatement pendingMatchLabel = null;
boolean debugTransPatterns;
private MethodSymbol currentMethodSym = null;
protected TransPatterns(Context context) {
context.put(transPatternsKey, this);
syms = Symtab.instance(context);
make = TreeMaker.instance(context);
types = Types.instance(context);
operators = Operators.instance(context);
log = Log.instance(context);
constFold = ConstFold.instance(context);
names = Names.instance(context);
target = Target.instance(context);
matchBindingsComputer = MatchBindingsComputer.instance(context);
debugTransPatterns = Options.instance(context).isSet("debug.patterns");
}
@Override
public void visitTypeTest(JCInstanceOf tree) {
if (tree.pattern.hasTag(Tag.BINDINGPATTERN)) {
//E instanceof T N
//=>
//(let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))
JCBindingPattern patt = (JCBindingPattern)tree.pattern;
VarSymbol pattSym = patt.symbol;
Type tempType = tree.expr.type.hasTag(BOT) ?
syms.objectType
: tree.expr.type;
VarSymbol temp = new VarSymbol(pattSym.flags() | Flags.SYNTHETIC,
names.fromString(pattSym.name.toString() + target.syntheticNameChar() + "temp"),
tempType,
patt.symbol.owner);
JCExpression translatedExpr = translate(tree.expr);
Type castTargetType = types.boxedTypeOrType(pattSym.erasure(types));
result = makeTypeTest(make.Ident(temp), make.Type(castTargetType));
VarSymbol bindingVar = bindingContext.getBindingFor(patt.symbol);
if (bindingVar != null) {
JCAssign fakeInit = (JCAssign)make.at(tree.pos).Assign(
make.Ident(bindingVar), convert(make.Ident(temp), castTargetType)).setType(bindingVar.erasure(types));
result = makeBinary(Tag.AND, (JCExpression)result,
makeBinary(Tag.EQ, fakeInit, convert(make.Ident(temp), castTargetType)));
}
result = make.at(tree.pos).LetExpr(make.VarDef(temp, translatedExpr), (JCExpression)result).setType(syms.booleanType);
((LetExpr) result).needsCond = true;
} else {
super.visitTypeTest(tree);
}
}
@Override
public void visitBinary(JCBinary tree) {
List<BindingSymbol> matchBindings;
switch (tree.getTag()) {
case AND:
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, true);
break;
case OR:
matchBindings = matchBindingsComputer.getMatchBindings(tree.lhs, false);
break;
default:
matchBindings = List.nil();
break;
}
bindingContext = new BasicBindingContext(matchBindings);
try {
super.visitBinary(tree);
result = bindingContext.decorateExpression(tree);
} finally {
bindingContext.pop();
}
}
@Override
public void visitConditional(JCConditional tree) {
bindingContext = new BasicBindingContext(
matchBindingsComputer.getMatchBindings(tree.cond, true)
.appendList(matchBindingsComputer.getMatchBindings(tree.cond, false)));
try {
super.visitConditional(tree);
result = bindingContext.decorateExpression(tree);
} finally {
bindingContext.pop();
}
}
@Override
public void visitIf(JCIf tree) {
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
try {
super.visitIf(tree);
result = bindingContext.decorateStatement(tree);
} finally {
bindingContext.pop();
}
}
@Override
public void visitForLoop(JCForLoop tree) {
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
try {
super.visitForLoop(tree);
result = bindingContext.decorateStatement(tree);
} finally {
bindingContext.pop();
}
}
@Override
public void visitWhileLoop(JCWhileLoop tree) {
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
try {
super.visitWhileLoop(tree);
result = bindingContext.decorateStatement(tree);
} finally {
bindingContext.pop();
}
}
@Override
public void visitDoLoop(JCDoWhileLoop tree) {
bindingContext = new BasicBindingContext(getMatchBindings(tree.cond));
try {
super.visitDoLoop(tree);
result = bindingContext.decorateStatement(tree);
} finally {
bindingContext.pop();
}
}
@Override
public void visitMethodDef(JCMethodDecl tree) {
MethodSymbol prevMethodSym = currentMethodSym;
try {
currentMethodSym = tree.sym;
super.visitMethodDef(tree);
} finally {
currentMethodSym = prevMethodSym;
}
}
@Override
public void visitIdent(JCIdent tree) {
VarSymbol bindingVar = null;
if ((tree.sym.flags() & Flags.MATCH_BINDING) != 0) {
bindingVar = bindingContext.getBindingFor((BindingSymbol)tree.sym);
}
if (bindingVar == null) {
super.visitIdent(tree);
} else {
result = make.at(tree.pos).Ident(bindingVar);
}
}
@Override
public void visitBlock(JCBlock tree) {
ListBuffer<JCStatement> statements = new ListBuffer<>();
bindingContext = new BasicBindingContext(List.nil()) {
boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
//{
// if (E instanceof T N) {
// return ;
// }
// //use of N:
//}
//=>
//{
// T N;
// if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) {
// return ;
// }
// //use of N:
//}
hoistedVarMap.put(binding, var.sym);
statements.append(var);
return true;
}
};
try {
for (List<JCStatement> l = tree.stats; l.nonEmpty(); l = l.tail) {
statements.append(translate(l.head));
}
tree.stats = statements.toList();
result = tree;
} finally {
bindingContext.pop();
}
}
public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
try {
this.make = make;
translate(cdef);
} finally {
// note that recursive invocations of this method fail hard
this.make = null;
}
return cdef;
}
/** Make an instanceof expression.
* @param lhs The expression.
* @param type The type to be tested.
*/
JCInstanceOf makeTypeTest(JCExpression lhs, JCExpression type) {
JCInstanceOf tree = make.TypeTest(lhs, type);
tree.type = syms.booleanType;
return tree;
}
/** Make an attributed binary expression (copied from Lower).
* @param optag The operators tree tag.
* @param lhs The operator's left argument.
* @param rhs The operator's right argument.
*/
JCBinary makeBinary(JCTree.Tag optag, JCExpression lhs, JCExpression rhs) {
JCBinary tree = make.Binary(optag, lhs, rhs);
tree.operator = operators.resolveBinary(tree, optag, lhs.type, rhs.type);
tree.type = tree.operator.type.getReturnType();
return tree;
}
JCExpression convert(JCExpression expr, Type target) {
JCExpression result = make.at(expr.pos()).TypeCast(make.Type(target), expr);
result.type = target;
return result;
}
private List<BindingSymbol> getMatchBindings(JCExpression cond) {
return matchBindingsComputer.getMatchBindings(cond, true)
.appendList(matchBindingsComputer.getMatchBindings(cond, false));
}
abstract class BindingContext {
abstract VarSymbol getBindingFor(BindingSymbol varSymbol);
abstract JCStatement decorateStatement(JCStatement stat);
abstract JCExpression decorateExpression(JCExpression expr);
abstract BindingContext pop();
abstract boolean tryPrepend(BindingSymbol binding, JCVariableDecl var);
}
class BasicBindingContext extends BindingContext {
List<BindingSymbol> matchBindings;
Map<BindingSymbol, VarSymbol> hoistedVarMap;
BindingContext parent;
public BasicBindingContext(List<BindingSymbol> matchBindings) {
this.matchBindings = matchBindings;
this.parent = bindingContext;
this.hoistedVarMap = matchBindings.stream()
.filter(v -> parent.getBindingFor(v) == null)
.collect(Collectors.toMap(v -> v, v -> {
VarSymbol res = new VarSymbol(v.flags(), v.name, v.type, v.owner);
res.setTypeAttributes(v.getRawTypeAttributes());
return res;
}));
}
@Override
VarSymbol getBindingFor(BindingSymbol varSymbol) {
VarSymbol res = parent.getBindingFor(varSymbol);
if (res != null) {
return res;
}
return hoistedVarMap.entrySet().stream()
.filter(e -> e.getKey().isAliasFor(varSymbol))
.findFirst()
.map(e -> e.getValue()).orElse(null);
}
@Override
JCStatement decorateStatement(JCStatement stat) {
if (hoistedVarMap.isEmpty()) return stat;
//if (E instanceof T N) {
// //use N
//}
//=>
//{
// T N;
// if ((let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp))) {
// //use N
// }
//}
ListBuffer<JCStatement> stats = new ListBuffer<>();
for (Entry<BindingSymbol, VarSymbol> e : hoistedVarMap.entrySet()) {
JCVariableDecl decl = makeHoistedVarDecl(stat.pos, e.getValue());
if (!e.getKey().isPreserved() ||
!parent.tryPrepend(e.getKey(), decl)) {
stats.add(decl);
}
}
if (stats.nonEmpty()) {
stats.add(stat);
stat = make.at(stat.pos).Block(0, stats.toList());
}
return stat;
}
@Override
JCExpression decorateExpression(JCExpression expr) {
//E instanceof T N && /*use of N*/
//=>
//(let T N; (let T' N$temp = E; N$temp instanceof T && (N = (T) N$temp == (T) N$temp)) && /*use of N*/)
for (VarSymbol vsym : hoistedVarMap.values()) {
expr = make.at(expr.pos).LetExpr(makeHoistedVarDecl(expr.pos, vsym), expr).setType(expr.type);
}
return expr;
}
@Override
BindingContext pop() {
return bindingContext = parent;
}
@Override
boolean tryPrepend(BindingSymbol binding, JCVariableDecl var) {
return false;
}
private JCVariableDecl makeHoistedVarDecl(int pos, VarSymbol varSymbol) {
return make.at(pos).VarDef(varSymbol, null);
}
}
}

View File

@ -567,6 +567,13 @@ public class TransTypes extends TreeTranslator {
result = tree;
}
public void visitBindingPattern(JCBindingPattern tree) {
if (tree.vartype != null) {
tree.vartype = translate(tree.vartype, null);
}
result = tree;
}
public void visitSwitchExpression(JCSwitchExpression tree) {
Type selsuper = types.supertype(tree.selector.type);
boolean enumSwitch = selsuper != null &&
@ -780,7 +787,7 @@ public class TransTypes extends TreeTranslator {
public void visitTypeTest(JCInstanceOf tree) {
tree.expr = translate(tree.expr, null);
tree.clazz = translate(tree.clazz, null);
tree.pattern = translate(tree.pattern, null);
result = tree;
}

View File

@ -35,6 +35,7 @@ import com.sun.tools.javac.tree.JCTree.JCAssert;
import com.sun.tools.javac.tree.JCTree.JCAssign;
import com.sun.tools.javac.tree.JCTree.JCAssignOp;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBindingPattern;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCBreak;
import com.sun.tools.javac.tree.JCTree.JCCase;
@ -252,6 +253,18 @@ public class TreeDiffer extends TreeScanner {
&& tree.operator == that.operator;
}
@Override
public void visitBindingPattern(JCBindingPattern tree) {
JCBindingPattern that = (JCBindingPattern) parameter;
result =
scan(tree.vartype, that.vartype)
&& tree.name == that.name;
if (!result) {
return;
}
equiv.put(tree.symbol, that.symbol);
}
@Override
public void visitBlock(JCBlock tree) {
JCBlock that = (JCBlock) parameter;
@ -591,7 +604,7 @@ public class TreeDiffer extends TreeScanner {
@Override
public void visitTypeTest(JCInstanceOf tree) {
JCInstanceOf that = (JCInstanceOf) parameter;
result = scan(tree.expr, that.expr) && scan(tree.clazz, that.clazz);
result = scan(tree.expr, that.expr) && scan(tree.pattern, that.pattern);
}
@Override

View File

@ -105,6 +105,12 @@ public class TreeHasher extends TreeScanner {
super.visitSelect(tree);
}
@Override
public void visitBindingPattern(JCTree.JCBindingPattern tree) {
symbolHashes.computeIfAbsent(tree.symbol, k -> symbolHashes.size());
super.visitBindingPattern(tree);
}
@Override
public void visitVarDef(JCVariableDecl tree) {
symbolHashes.computeIfAbsent(tree.sym, k -> symbolHashes.size());

View File

@ -473,7 +473,7 @@ implements CRTFlags {
public void visitTypeTest(JCInstanceOf tree) {
SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
sr.mergeWith(csp(tree.expr));
sr.mergeWith(csp(tree.clazz));
sr.mergeWith(csp(tree.pattern));
result = sr;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2019, 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
@ -50,6 +50,7 @@ import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_Methodref;
import static com.sun.tools.javac.jvm.ClassFile.CONSTANT_String;
import static com.sun.tools.javac.jvm.UninitializedType.*;
import static com.sun.tools.javac.jvm.ClassWriter.StackMapTableFrame;
import java.util.Arrays;
/** An internal structure that corresponds to the code attribute of
* methods in a classfile. The class also provides some utility operations to
@ -2075,6 +2076,7 @@ public class Code {
lvar[adr] = v.dup();
v.closeRange(length);
putVar(v);
fillLocalVarPosition(v);
} else {
v.removeLastRange();
}
@ -2106,20 +2108,31 @@ public class Code {
private void fillLocalVarPosition(LocalVar lv) {
if (lv == null || lv.sym == null || lv.sym.isExceptionParameter()|| !lv.sym.hasTypeAnnotations())
return;
LocalVar.Range widestRange = lv.getWidestRange();
LocalVar.Range[] validRanges = lv.aliveRanges.stream().filter(r -> r.closed() && r.length > 0).toArray(s -> new LocalVar.Range[s]);
if (validRanges.length == 0)
return ;
int[] lvarOffset = Arrays.stream(validRanges).mapToInt(r -> r.start_pc).toArray();
int[] lvarLength = Arrays.stream(validRanges).mapToInt(r -> r.length).toArray();
int[] lvarIndex = Arrays.stream(validRanges).mapToInt(r -> lv.reg).toArray();
for (Attribute.TypeCompound ta : lv.sym.getRawTypeAttributes()) {
TypeAnnotationPosition p = ta.position;
if (widestRange.closed() && widestRange.length > 0) {
p.lvarOffset = new int[] { (int)widestRange.start_pc };
p.lvarLength = new int[] { (int)widestRange.length };
p.lvarIndex = new int[] { (int)lv.reg };
p.isValidOffset = true;
} else {
p.isValidOffset = false;
}
p.lvarOffset = appendArray(p.lvarOffset, lvarOffset);
p.lvarLength = appendArray(p.lvarLength, lvarLength);
p.lvarIndex = appendArray(p.lvarIndex, lvarIndex);
p.isValidOffset = true;
}
}
private int[] appendArray(int[] source, int[] append) {
if (source == null || source.length == 0) return append;
int[] result = new int[source.length + append.length];
System.arraycopy(source, 0, result, 0, source.length);
System.arraycopy(append, 0, result, source.length, append.length);
return result;
}
// Method to be called after compressCatchTable to
// fill in the exception table index for type
// annotations on exception parameters.

View File

@ -2216,7 +2216,7 @@ public class Gen extends JCTree.Visitor {
public void visitTypeTest(JCInstanceOf tree) {
genExpr(tree.expr, tree.expr.type).load();
setTypeAnnotationPositions(tree.pos);
code.emitop2(instanceof_, makeRef(tree.pos(), tree.clazz.type));
code.emitop2(instanceof_, makeRef(tree.pos(), tree.pattern.type));
result = items.makeStackItem(syms.booleanType);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2019, 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
@ -1558,6 +1558,12 @@ public class JavaCompiler {
env.tree = transTypes.translateTopLevelClass(env.tree, localMake);
compileStates.put(env, CompileState.TRANSTYPES);
if (shouldStop(CompileState.TRANSPATTERNS))
return;
env.tree = TransPatterns.instance(context).translateTopLevelClass(env, env.tree, localMake);
compileStates.put(env, CompileState.TRANSPATTERNS);
if (Feature.LAMBDA.allowedInSource(source) && scanner.hasLambdas) {
if (shouldStop(CompileState.UNLAMBDA))
return;

View File

@ -893,6 +893,7 @@ public class JavacParser implements Parser {
/* Expression2Rest = {infixop Expression3}
* | Expression3 instanceof Type
* | Expression3 instanceof Pattern
* infixop = "||"
* | "&&"
* | "|"
@ -915,13 +916,24 @@ public class JavacParser implements Parser {
Token topOp = Tokens.DUMMY;
while (prec(token.kind) >= minprec) {
opStack[top] = topOp;
top++;
topOp = token;
nextToken();
odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3();
if (token.kind == INSTANCEOF) {
int pos = token.pos;
nextToken();
JCTree pattern = parseType();
if (token.kind == IDENTIFIER) {
checkSourceLevel(token.pos, Feature.PATTERN_MATCHING_IN_INSTANCEOF);
pattern = toP(F.at(token.pos).BindingPattern(ident(), pattern));
}
odStack[top] = F.at(pos).TypeTest(odStack[top], pattern);
} else {
topOp = token;
nextToken();
top++;
odStack[top] = term3();
}
while (top > 0 && prec(topOp.kind) >= prec(token.kind)) {
odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1],
odStack[top]);
odStack[top - 1] = F.at(topOp.pos).Binary(optag(topOp.kind), odStack[top - 1], odStack[top]);
top--;
topOp = opStack[top];
}
@ -938,19 +950,6 @@ public class JavacParser implements Parser {
return t;
}
//where
/** Construct a binary or type test node.
*/
private JCExpression makeOp(int pos,
TokenKind topOp,
JCExpression od1,
JCExpression od2)
{
if (topOp == INSTANCEOF) {
return F.at(pos).TypeTest(od1, od2);
} else {
return F.at(pos).Binary(optag(topOp), od1, od2);
}
}
/** If tree is a concatenation of string literals, replace it
* by a single literal representing the concatenated string.
*/

View File

@ -545,6 +545,10 @@ compiler.err.final.parameter.may.not.be.assigned=\
compiler.err.try.resource.may.not.be.assigned=\
auto-closeable resource {0} may not be assigned
# 0: symbol
compiler.err.pattern.binding.may.not.be.assigned=\
pattern binding {0} may not be assigned
# 0: symbol
compiler.err.multicatch.parameter.may.not.be.assigned=\
multi-catch parameter {0} may not be assigned
@ -1416,6 +1420,10 @@ compiler.err.varargs.invalid.trustme.anno=\
compiler.misc.varargs.trustme.on.reifiable.varargs=\
Varargs element type {0} is reifiable.
# 0: type, 1: type
compiler.err.instanceof.reifiable.not.safe=\
{0} cannot be safely cast to {1}
# 0: symbol
compiler.misc.varargs.trustme.on.non.varargs.meth=\
Method {0} is not a varargs method.
@ -2909,6 +2917,12 @@ compiler.misc.feature.switch.expressions=\
compiler.misc.feature.var.syntax.in.implicit.lambda=\
var syntax in implicit lambdas
compiler.misc.feature.pattern.matching.instanceof=\
pattern matching in instanceof
compiler.misc.feature.reifiable.types.instanceof=\
reifiable types in instanceof
compiler.warn.underscore.as.identifier=\
as of release 9, ''_'' is a keyword, and may not be used as an identifier
@ -3399,6 +3413,9 @@ compiler.warn.leaks.not.accessible.unexported.qualified=\
compiler.err.illegal.argument.for.option=\
illegal argument for {0}: {1}
compiler.err.match.binding.exists=\
illegal attempt to redefine an existing match binding
compiler.err.switch.null.not.allowed=\
null label in case is not allowed

View File

@ -38,6 +38,7 @@ import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Directive.RequiresDirective;
import com.sun.tools.javac.code.Scope.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.comp.MatchBindingsComputer.BindingSymbol;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.DefinedBy.Api;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
@ -238,6 +239,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
*/
TYPETEST,
/** Patterns.
*/
BINDINGPATTERN,
/** Indexed array expressions, of type Indexed.
*/
INDEXED,
@ -2135,10 +2140,10 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
*/
public static class JCInstanceOf extends JCExpression implements InstanceOfTree {
public JCExpression expr;
public JCTree clazz;
protected JCInstanceOf(JCExpression expr, JCTree clazz) {
public JCTree pattern;
protected JCInstanceOf(JCExpression expr, JCTree pattern) {
this.expr = expr;
this.clazz = clazz;
this.pattern = pattern;
}
@Override
public void accept(Visitor v) { v.visitTypeTest(this); }
@ -2146,7 +2151,13 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() { return Kind.INSTANCE_OF; }
@DefinedBy(Api.COMPILER_TREE)
public JCTree getType() { return clazz; }
public JCTree getType() { return pattern instanceof JCPattern ? pattern.hasTag(BINDINGPATTERN) ? ((JCBindingPattern) pattern).vartype : null : pattern; }
@Override @DefinedBy(Api.COMPILER_TREE)
public JCPattern getPattern() {
return pattern instanceof JCPattern ? (JCPattern) pattern : null;
}
@DefinedBy(Api.COMPILER_TREE)
public JCExpression getExpression() { return expr; }
@Override @DefinedBy(Api.COMPILER_TREE)
@ -2159,6 +2170,60 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
}
}
/**
* Pattern matching forms.
*/
public static abstract class JCPattern extends JCTree
implements PatternTree {
public JCExpression constExpression() {
return null;
}
}
public static class JCBindingPattern extends JCPattern
implements BindingPatternTree {
public Name name;
public BindingSymbol symbol;
public JCTree vartype;
protected JCBindingPattern(Name name, BindingSymbol symbol, JCTree vartype) {
this.name = name;
this.symbol = symbol;
this.vartype = vartype;
}
@DefinedBy(Api.COMPILER_TREE)
public Name getBinding() {
return name;
}
@Override @DefinedBy(Api.COMPILER_TREE)
public Tree getType() {
return vartype;
}
@Override
public void accept(Visitor v) {
v.visitBindingPattern(this);
}
@DefinedBy(Api.COMPILER_TREE)
public Kind getKind() {
return Kind.BINDING_PATTERN;
}
@Override
@DefinedBy(Api.COMPILER_TREE)
public <R, D> R accept(TreeVisitor<R, D> v, D d) {
return v.visitBindingPattern(this, d);
}
@Override
public Tag getTag() {
return BINDINGPATTERN;
}
}
/**
* An array selection
*/
@ -3133,6 +3198,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
JCBinary Binary(Tag opcode, JCExpression lhs, JCExpression rhs);
JCTypeCast TypeCast(JCTree expr, JCExpression type);
JCInstanceOf TypeTest(JCExpression expr, JCTree clazz);
JCBindingPattern BindingPattern(Name name, JCTree vartype);
JCArrayAccess Indexed(JCExpression indexed, JCExpression index);
JCFieldAccess Select(JCExpression selected, Name selector);
JCIdent Ident(Name idname);
@ -3197,6 +3263,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public void visitBinary(JCBinary that) { visitTree(that); }
public void visitTypeCast(JCTypeCast that) { visitTree(that); }
public void visitTypeTest(JCInstanceOf that) { visitTree(that); }
public void visitBindingPattern(JCBindingPattern that) { visitTree(that); }
public void visitIndexed(JCArrayAccess that) { visitTree(that); }
public void visitSelect(JCFieldAccess that) { visitTree(that); }
public void visitReference(JCMemberReference that) { visitTree(that); }

View File

@ -234,6 +234,14 @@ public class Pretty extends JCTree.Visitor {
printExprs(trees, ", ");
}
/** Derived visitor method: print pattern.
*/
public void printPattern(JCTree tree) throws IOException {
printExpr(tree);
}
/** Derived visitor method: print list of statements, each on a separate line.
*/
public void printStats(List<? extends JCTree> trees) throws IOException {
@ -877,6 +885,16 @@ public class Pretty extends JCTree.Visitor {
}
}
public void visitBindingPattern(JCBindingPattern patt) {
try {
printExpr(patt.vartype);
print(" ");
print(patt.name);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void visitSynchronized(JCSynchronized tree) {
try {
print("synchronized ");
@ -1283,7 +1301,11 @@ public class Pretty extends JCTree.Visitor {
open(prec, TreeInfo.ordPrec);
printExpr(tree.expr, TreeInfo.ordPrec);
print(" instanceof ");
printExpr(tree.clazz, TreeInfo.ordPrec + 1);
if (tree.pattern instanceof JCPattern) {
printPattern(tree.pattern);
} else {
printExpr(tree.getType(), TreeInfo.ordPrec + 1);
}
close(prec, TreeInfo.ordPrec);
} catch (IOException e) {
throw new UncheckedIOException(e);

View File

@ -26,7 +26,6 @@
package com.sun.tools.javac.tree;
import com.sun.source.tree.*;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
@ -481,8 +480,15 @@ public class TreeCopier<P> implements TreeVisitor<JCTree,P> {
public JCTree visitInstanceOf(InstanceOfTree node, P p) {
JCInstanceOf t = (JCInstanceOf) node;
JCExpression expr = copy(t.expr, p);
JCTree clazz = copy(t.clazz, p);
return M.at(t.pos).TypeTest(expr, clazz);
JCTree pattern = copy(t.pattern, p);
return M.at(t.pos).TypeTest(expr, pattern);
}
@DefinedBy(Api.COMPILER_TREE)
public JCTree visitBindingPattern(BindingPatternTree node, P p) {
JCBindingPattern t = (JCBindingPattern) node;
JCTree vartype = copy(t.vartype, p);
return M.at(t.pos).BindingPattern(t.name, vartype);
}
@DefinedBy(Api.COMPILER_TREE)

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2019, 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
@ -490,6 +490,10 @@ public class TreeInfo {
return getStartPos(node.vartype);
}
}
case BINDINGPATTERN: {
JCBindingPattern node = (JCBindingPattern)tree;
return getStartPos(node.vartype);
}
case ERRONEOUS: {
JCErroneous node = (JCErroneous)tree;
if (node.errs != null && node.errs.nonEmpty())
@ -574,7 +578,7 @@ public class TreeInfo {
case TYPECAST:
return getEndPos(((JCTypeCast) tree).expr, endPosTable);
case TYPETEST:
return getEndPos(((JCInstanceOf) tree).clazz, endPosTable);
return getEndPos(((JCInstanceOf) tree).pattern, endPosTable);
case WHILELOOP:
return getEndPos(((JCWhileLoop) tree).body, endPosTable);
case ANNOTATED_TYPE:
@ -847,6 +851,8 @@ public class TreeInfo {
if (node.type != null)
return node.type.tsym;
return null;
case BINDINGPATTERN:
return ((JCBindingPattern) node).symbol;
default:
return null;
}
@ -1225,4 +1231,5 @@ public class TreeInfo {
public static boolean isPackageInfo(JCCompilationUnit tree) {
return tree.sourcefile.isNameCompatible("package-info", JavaFileObject.Kind.SOURCE);
}
}

View File

@ -29,7 +29,6 @@ import java.util.Iterator;
import com.sun.source.tree.CaseTree;
import com.sun.source.tree.ModuleTree.ModuleKind;
import com.sun.source.tree.Tree.Kind;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Attribute.UnresolvedClass;
import com.sun.tools.javac.code.Symbol.*;
@ -465,6 +464,12 @@ public class TreeMaker implements JCTree.Factory {
return tree;
}
public JCBindingPattern BindingPattern(Name name, JCTree vartype) {
JCBindingPattern tree = new JCBindingPattern(name, null, vartype);
tree.pos = pos;
return tree;
}
public JCArrayAccess Indexed(JCExpression indexed, JCExpression index) {
JCArrayAccess tree = new JCArrayAccess(indexed, index);
tree.pos = pos;

View File

@ -299,7 +299,12 @@ public class TreeScanner extends Visitor {
public void visitTypeTest(JCInstanceOf tree) {
scan(tree.expr);
scan(tree.clazz);
scan(tree.pattern);
}
public void visitBindingPattern(JCBindingPattern tree) {
if (tree.vartype != null)
scan(tree.vartype);
}
public void visitIndexed(JCArrayAccess tree) {

View File

@ -354,7 +354,12 @@ public class TreeTranslator extends JCTree.Visitor {
public void visitTypeTest(JCInstanceOf tree) {
tree.expr = translate(tree.expr);
tree.clazz = translate(tree.clazz);
tree.pattern = translate(tree.pattern);
result = tree;
}
public void visitBindingPattern(JCBindingPattern tree) {
tree.vartype = translate(tree.vartype);
result = tree;
}

View File

@ -24,10 +24,11 @@
/**
* @test
* @summary smoke tests for CallSite
* @library /test/lib
*
* @build indify.Indify
* @compile CallSiteTest.java
* @run main/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
* @run main/othervm/timeout=3600 -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies -Xbatch
* indify.Indify
* --expand-properties --classpath ${test.classes}
* --java test.java.lang.invoke.CallSiteTest
@ -40,6 +41,7 @@ import java.io.*;
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;
import static jdk.test.lib.Asserts.*;
public class CallSiteTest {
private static final Class<?> CLASS = CallSiteTest.class;
@ -51,16 +53,19 @@ public class CallSiteTest {
static {
try {
mh_foo = lookup().findStatic(CLASS, "foo", methodType(int.class, int.class, int.class));
mh_bar = lookup().findStatic(CLASS, "bar", methodType(int.class, int.class, int.class));
mcs = new MutableCallSite(mh_foo);
vcs = new VolatileCallSite(mh_foo);
} catch (Exception e) {
e.printStackTrace();
throw new Error(e);
}
}
public static void main(String... av) throws Throwable {
testConstantCallSite();
testMutableCallSite();
testVolatileCallSite();
}
@ -69,9 +74,61 @@ public class CallSiteTest {
private static final int RESULT1 = 762786192;
private static final int RESULT2 = -21474836;
private static void assertEquals(int expected, int actual) {
if (expected != actual)
throw new AssertionError("expected: " + expected + ", actual: " + actual);
static final CallSite MCS = new MutableCallSite(methodType(void.class));
static final MethodHandle MCS_INVOKER = MCS.dynamicInvoker();
static void test(boolean shouldThrow) {
try {
MCS_INVOKER.invokeExact();
if (shouldThrow) {
throw new AssertionError("should throw");
}
} catch (IllegalStateException ise) {
if (!shouldThrow) {
throw new AssertionError("should not throw", ise);
}
} catch (Throwable e) {
throw new Error(e);
}
}
static class MyCCS extends ConstantCallSite {
public MyCCS(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
super(targetType, createTargetHook);
}
}
private static MethodHandle testConstantCallSiteHandler(CallSite cs, CallSite[] holder) throws Throwable {
holder[0] = cs; // capture call site instance for subsequent checks
MethodType csType = cs.type(); // should not throw on partially constructed instance
// Truly dynamic invoker for constant call site
MethodHandle getTarget = lookup().findVirtual(CallSite.class, "getTarget", MethodType.methodType(MethodHandle.class))
.bindTo(cs);
MethodHandle invoker = MethodHandles.exactInvoker(csType);
MethodHandle target = MethodHandles.foldArguments(invoker, getTarget);
MCS.setTarget(target);
// warmup
for (int i = 0; i < 20_000; i++) {
test(true); // should throw IllegalStateException
}
return MethodHandles.empty(csType); // initialize cs with an empty method handle
}
private static void testConstantCallSite() throws Throwable {
CallSite[] holder = new CallSite[1];
MethodHandle handler = lookup().findStatic(CLASS, "testConstantCallSiteHandler", MethodType.methodType(MethodHandle.class, CallSite.class, CallSite[].class));
handler = MethodHandles.insertArguments(handler, 1, new Object[] { holder } );
CallSite ccs = new MyCCS(MCS.type(), handler); // trigger call to handler
if (ccs != holder[0]) {
throw new AssertionError("different call site instances");
}
test(false); // should not throw
}
private static void testMutableCallSite() throws Throwable {
@ -83,11 +140,11 @@ public class CallSiteTest {
for (int n = 0; n < 2; n++) {
mcs.setTarget(mh_foo);
for (int i = 0; i < 5; i++) {
assertEquals(RESULT1, runMutableCallSite());
assertEQ(RESULT1, runMutableCallSite());
}
mcs.setTarget(mh_bar);
for (int i = 0; i < 5; i++) {
assertEquals(RESULT2, runMutableCallSite());
assertEQ(RESULT2, runMutableCallSite());
}
}
}
@ -100,11 +157,11 @@ public class CallSiteTest {
for (int n = 0; n < 2; n++) {
vcs.setTarget(mh_foo);
for (int i = 0; i < 5; i++) {
assertEquals(RESULT1, runVolatileCallSite());
assertEQ(RESULT1, runVolatileCallSite());
}
vcs.setTarget(mh_bar);
for (int i = 0; i < 5; i++) {
assertEquals(RESULT2, runVolatileCallSite());
assertEQ(RESULT2, runVolatileCallSite());
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2019, 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,12 +27,14 @@ import java.net.URL;
import java.util.List;
import com.sun.tools.classfile.*;
import java.util.ArrayList;
public class ClassfileTestHelper {
int expected_tinvisibles = 0;
int expected_tvisibles = 0;
int expected_invisibles = 0;
int expected_visibles = 0;
List<String> extraOptions = List.of();
//Makes debugging much easier. Set to 'false' for less output.
public Boolean verbose = true;
@ -48,8 +50,9 @@ public class ClassfileTestHelper {
}
File compile(File f) {
int rc = com.sun.tools.javac.Main.compile(new String[] {
"-g", f.getPath() });
List<String> options = new ArrayList<>(List.of("-g", f.getPath()));
options.addAll(extraOptions);
int rc = com.sun.tools.javac.Main.compile(options.toArray(new String[0]));
if (rc != 0)
throw new Error("compilation failed. rc=" + rc);
String path = f.getPath();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2019, 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,9 +30,13 @@
import com.sun.tools.classfile.*;
import java.io.File;
import java.util.List;
public class CombinationsTargetTest2 extends ClassfileTestHelper {
private static final String JDK_VERSION =
Integer.toString(Runtime.getRuntime().version().feature());
// Test count helps identify test case in event of failure.
int testcount = 0;
@ -45,7 +49,9 @@ public class CombinationsTargetTest2 extends ClassfileTestHelper {
src5("(repeating) type annotations on field in anonymous class", false),
src6("(repeating) type annotations on void method declaration", false),
src7("(repeating) type annotations in use of instanceof", true),
src8("(repeating) type annotations in use of instanceof in method", true);
src7p("(repeating) type annotations in use of instanceof with type test pattern", true),
src8("(repeating) type annotations in use of instanceof in method", true),
src8p("(repeating) type annotations in use of instanceof with type test pattern in method", true);
String description;
Boolean local;
@ -92,8 +98,12 @@ public class CombinationsTargetTest2 extends ClassfileTestHelper {
test( 0, 0, 0, 2, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src6);
test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7);
test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7);
test( 2, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src7p);
test( 0, 2, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src7p);
test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8);
test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8);
test( 4, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src8p);
test( 0, 4, 0, 0, As, BDs, ABMix, "RUNTIME", et, ++testrun, srce.src8p);
break;
case "FIELD":
test( 8, 0, 0, 0, As, BDs, ABMix, "CLASS", et, ++testrun, srce.src1);
@ -122,6 +132,7 @@ public class CombinationsTargetTest2 extends ClassfileTestHelper {
expected_tinvisibles = tinv;
expected_visibles = vis;
expected_invisibles = inv;
extraOptions = List.of();
File testFile = null;
String tname="Test" + N.toString();
hasInnerClass=false;
@ -385,6 +396,24 @@ public class CombinationsTargetTest2 extends ClassfileTestHelper {
"\n\n";
hasInnerClass=false;
break;
case src7p: // (repeating) type annotations in use of instanceof with type test pattern
/*
* class Test10{
* String data = "test";
* boolean dataIsString = ( data instanceof @A @B @A @B String str);
* }
*/
source = new String( source +
"// " + src.description + "\n" +
"class "+ testname + "{\n" +
" String data = \"test\";\n" +
" boolean dataIsString = ( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" +
"}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
"\n\n";
extraOptions = List.of("--enable-preview",
"-source", JDK_VERSION);
hasInnerClass=false;
break;
case src8: // (repeating) type annotations in use of instanceof
/*
* class Test20{
@ -411,6 +440,34 @@ public class CombinationsTargetTest2 extends ClassfileTestHelper {
"\n\n";
hasInnerClass=false;
break;
case src8p: // (repeating) type annotations in use of instanceof with type test pattern
/*
* class Test20{
* String data = "test";
* Boolean isString() {
* if( data instanceof @A @B @A @B String )
* return true;
* else
* return( data instanceof @A @B @A @B String );
* }
* }
*/
source = new String( source +
"// " + src.description + "\n" +
"class "+ testname + "{\n" +
" String data = \"test\";\n" +
" Boolean isString() { \n" +
" if( data instanceof _As_ _Bs_ String str)\n" +
" return true;\n" +
" else\n" +
" return( data instanceof _As_ _Bs_ String str && str.isEmpty());\n" +
" }\n" +
"}\n").concat(sourceBase).replace("_OTHER_", annot2).replace("_As_",As).replace("_Bs_",Bs) +
"\n\n";
extraOptions = List.of("--enable-preview",
"-source", JDK_VERSION);
hasInnerClass=false;
break;
}
return imports + source;

View File

@ -0,0 +1,222 @@
/*
* Copyright (c) 2009, 2019, 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
* @summary Verify type annotation on binding patterns
* @library /tools/lib
* @modules java.compiler
* jdk.jdeps/com.sun.tools.javap
* @build toolbox.JavapTask
* @compile --enable-preview -source ${jdk.version} Patterns.java
* @run main/othervm --enable-preview Patterns
*/
import java.lang.annotation.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import toolbox.JavapTask;
import toolbox.Task;
import toolbox.ToolBox;
public class Patterns {
private ToolBox tb = new ToolBox();
public static void main(String[] args) throws Exception {
new Patterns().run();
}
public void run() throws Exception {
String out = new JavapTask(tb)
.options("-private",
"-verbose")
.classpath(System.getProperty("test.classes"))
.classes("Patterns$SimpleBindingPattern")
.run()
.getOutputLines(Task.OutputKind.DIRECT)
.stream()
.collect(Collectors.joining("\n"));
String constantPool = out.substring(0, out.indexOf('{'));
out = out.replaceAll("(?ms) *Code:.*?\n( *RuntimeInvisibleTypeAnnotations:)", "$1");
out = out.substring(out.indexOf('{'));
out = out.substring(0, out.lastIndexOf('}') + 1);
String A = snipCPNumber(constantPool, "LPatterns$SimpleBindingPattern$A;");
String CA = snipCPNumber(constantPool, "LPatterns$SimpleBindingPattern$CA;");
String value = snipCPNumber(constantPool, "value");
String expected = """
{
private static final java.lang.Object o;
descriptor: Ljava/lang/Object;
flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
private static final boolean B1s;
descriptor: Z
flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
private static final boolean B1m;
descriptor: Z
flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
private final boolean B2s;
descriptor: Z
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
private final boolean B2m;
descriptor: Z
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
public Patterns$SimpleBindingPattern();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
RuntimeInvisibleTypeAnnotations:
0: #_A_(): LOCAL_VARIABLE, {start_pc=257, length=18, index=2}
Patterns$SimpleBindingPattern$A
1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=297, length=19, index=3}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
2: #_A_(): LOCAL_VARIABLE, {start_pc=22, length=18, index=1}
Patterns$SimpleBindingPattern$A
3: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=62, length=18, index=1}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
4: #_A_(): LOCAL_VARIABLE, {start_pc=101, length=18, index=2}
Patterns$SimpleBindingPattern$A
5: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=141, length=19, index=3}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
6: #_A_(): LOCAL_VARIABLE, {start_pc=179, length=18, index=2}
Patterns$SimpleBindingPattern$A
7: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=219, length=19, index=3}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
void testPatterns();
descriptor: ()V
flags: (0x0000)
RuntimeInvisibleTypeAnnotations:
0: #_A_(): LOCAL_VARIABLE, {start_pc=17, length=18, index=2}
Patterns$SimpleBindingPattern$A
1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=57, length=19, index=3}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
void testPatternsDesugared();
descriptor: ()V
flags: (0x0000)
RuntimeInvisibleTypeAnnotations:
0: #_A_(): LOCAL_VARIABLE, {start_pc=17, length=15, index=1; start_pc=51, length=15, index=1}
Patterns$SimpleBindingPattern$A
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
RuntimeInvisibleTypeAnnotations:
0: #_A_(): LOCAL_VARIABLE, {start_pc=22, length=18, index=0}
Patterns$SimpleBindingPattern$A
1: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=61, length=18, index=0}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
2: #_A_(): LOCAL_VARIABLE, {start_pc=100, length=18, index=1}
Patterns$SimpleBindingPattern$A
3: #_CA_(#_value_=[@#_A_(),@#_A_()]): LOCAL_VARIABLE, {start_pc=137, length=18, index=2}
Patterns$SimpleBindingPattern$CA(
value=[@Patterns$SimpleBindingPattern$A,@Patterns$SimpleBindingPattern$A]
)
}""".replace("_A_", A).replace("_CA_", CA).replace("_value_", value);
if (!expected.equals(out)) {
throw new AssertionError("Unexpected output:\n" + out + "\nexpected:\n" + expected);
}
}
private String snipCPNumber(String constantPool, String expectedConstant) {
Matcher m = Pattern.compile("#([0-9]+).*" + Pattern.quote(expectedConstant))
.matcher(constantPool);
if (!m.find()) {
throw new AssertionError("Cannot find constant pool item");
}
return m.group(1);
}
/*********************** Test class *************************/
static class SimpleBindingPattern {
@Target(ElementType.TYPE_USE)
@Repeatable(CA.class)
@interface A {}
@Target(ElementType.TYPE_USE)
@interface CA {
public A[] value();
}
private static final Object o = "";
private static final boolean B1s = o instanceof @A String s && s.isEmpty();
private static final boolean B1m = o instanceof @A @A String s && s.isEmpty();
private final boolean B2s = o instanceof @A String s && s.isEmpty();
private final boolean B2m = o instanceof @A @A String s && s.isEmpty();
static {
boolean B3s = o instanceof @A String s && s.isEmpty();
boolean B3m = o instanceof @A @A String s && s.isEmpty();
}
{
boolean B4s = o instanceof @A String s && s.isEmpty();
boolean B4m = o instanceof @A @A String s && s.isEmpty();
}
{
boolean B5s = o instanceof @A String s && s.isEmpty();
boolean B5m = o instanceof @A @A String s && s.isEmpty();
}
public SimpleBindingPattern() {
boolean B6s = o instanceof @A String s && s.isEmpty();
boolean B6m = o instanceof @A @A String s && s.isEmpty();
}
void testPatterns() {
boolean B7s = o instanceof @A String s && s.isEmpty();
boolean B7m = o instanceof @A @A String s && s.isEmpty();
}
void testPatternsDesugared() {
@A String s;
boolean B8s = o instanceof String && (s = (String) o) == s && s.isEmpty();
boolean B8sx = o instanceof String && (s = (String) o) == s && s.isEmpty();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2019, 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
@ -51,6 +51,9 @@ import javax.tools.ToolProvider;
public class TestGetElementReference {
private static final String JDK_VERSION =
Integer.toString(Runtime.getRuntime().version().feature());
public static void main(String... args) throws IOException {
analyze("TestGetElementReferenceData.java");
analyze("mod/module-info.java", "mod/api/pkg/Api.java");
@ -66,7 +69,10 @@ public class TestGetElementReference {
}
}
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
JavacTask ct = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, Arrays.asList("-Xjcov"), null, files);
List<String> options = List.of("-Xjcov",
"--enable-preview",
"-source", JDK_VERSION);
JavacTask ct = (JavacTask) ToolProvider.getSystemJavaCompiler().getTask(null, null, diagnostics, options, null, files);
Trees trees = Trees.instance(ct);
CompilationUnitTree cut = ct.parse().iterator().next();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2019, 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
@ -35,6 +35,8 @@ public class TestGetElementReferenceData {
java.util.List< /*getElement:INTERFACE:java.util.List*/ String> l;
utility/*getElement:METHOD:test.TestGetElementReferenceData.Base.utility()*/();
target(TestGetElementReferenceData :: test/*getElement:METHOD:test.TestGetElementReferenceData.test()*/);
Object/*getElement:CLASS:java.lang.Object*/ o = null;
if (o/*getElement:LOCAL_VARIABLE:o*/ instanceof String/*getElement:CLASS:java.lang.String*/ str/*getElement:LOCAL_VARIABLE:str*/) ;
}
private static void target(Runnable r) { r.run(); }
public static class Base {

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2019, 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.
*/
// key: compiler.err.instanceof.reifiable.not.safe
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source ${jdk.version}
import java.util.List;
class InstanceofReifiableNotSafe {
boolean test(Object o) {
return o instanceof List<String> l;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2017, 2019, 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.
*/
// key: compiler.err.match.binding.exists
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source ${jdk.version}
class MatchBindingExists {
public void test(Object o1, Object o2) {
if (o1 instanceof String k && o2 instanceof Integer k) {}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2017, 2019, 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.
*/
// key: compiler.err.pattern.binding.may.not.be.assigned
// key: compiler.note.preview.filename
// key: compiler.note.preview.recompile
// options: --enable-preview -source ${jdk.version}
class ResourceMayNotBeAssigned {
void m(Object o) {
if (o instanceof String s) {
s = "";
}
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019, 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.
*/
// key: compiler.misc.feature.pattern.matching.instanceof
// key: compiler.warn.preview.feature.use
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class PatternMatchingInstanceof {
boolean m(Object o) {
return o instanceof String s && s.isEmpty();
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, 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.
*/
// key: compiler.misc.feature.reifiable.types.instanceof
// key: compiler.warn.preview.feature.use.plural
// options: --enable-preview -source ${jdk.version} -Xlint:preview
class PatternMatchingInstanceof {
boolean m(I<String> i) {
return i instanceof C<String>;
}
interface I<T> {}
class C<T> implements I<T> {}
}

View File

@ -163,6 +163,9 @@ public class Deduplication {
group((Function<Integer, Integer>) x -> switch (x) { default: yield x; },
(Function<Integer, Integer>) x -> switch (x) { default: yield x; });
group((Function<Object, Integer>) x -> x instanceof Integer i ? i : -1,
(Function<Object, Integer>) x -> x instanceof Integer i ? i : -1);
}
void f() {}

View File

@ -880,7 +880,7 @@ public class DPrinter {
@Override
public void visitTypeTest(JCInstanceOf tree) {
printTree("expr", tree.expr);
printTree("clazz", tree.clazz);
printTree("pattern", tree.pattern);
}
@Override

View File

@ -0,0 +1,30 @@
/*
* @test /nodynamiccopyright/
* @bug 8231827
* @summary Clashing bindings are reported correctly
* @compile/fail/ref=BindingsExistTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsExistTest.java
*/
public class BindingsExistTest {
public void t(Object o1, Object o2) {
if (o1 instanceof String k && o2 instanceof Integer k) {}
if (o1 instanceof String k || o2 instanceof Integer k) {}
if (!(o1 instanceof String k)) {
return ;
}
if (o1 instanceof Integer k) {}
String s2 = "";
if (o1 instanceof String s2) {}
if (o1 instanceof String s3) {
String s3 = "";
}
if (!(o1 instanceof String s4)) {
return ;
}
String s4 = "";
}
}

View File

@ -0,0 +1,9 @@
BindingsExistTest.java:9:36: compiler.err.match.binding.exists
BindingsExistTest.java:11:36: compiler.err.match.binding.exists
BindingsExistTest.java:16:35: compiler.err.already.defined: kindname.variable, k, kindname.method, t(java.lang.Object,java.lang.Object)
BindingsExistTest.java:19:34: compiler.err.already.defined: kindname.variable, s2, kindname.method, t(java.lang.Object,java.lang.Object)
BindingsExistTest.java:22:20: compiler.err.already.defined: kindname.variable, s3, kindname.method, t(java.lang.Object,java.lang.Object)
BindingsExistTest.java:28:16: compiler.err.already.defined: kindname.variable, s4, kindname.method, t(java.lang.Object,java.lang.Object)
- compiler.note.preview.filename: BindingsExistTest.java
- compiler.note.preview.recompile
6 errors

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2017, 2019, 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 8231827
* @summary Basic tests for bindings from instanceof
* @compile --enable-preview -source ${jdk.version} BindingsTest1.java
* @run main/othervm --enable-preview BindingsTest1
*/
public class BindingsTest1 {
public static boolean Ktrue() { return true; }
public static void main(String[] args) {
Object o1 = "hello";
Integer i = 42;
Object o2 = i;
Object o3 = "there";
// Test for (e matches P).T = { binding variables in P }
if (o1 instanceof String s) {
s.length();
}
// Test for e1 && e2.T = union(e1.T, e2.T)
if (o1 instanceof String s && o2 instanceof Integer in) {
s.length();
in.intValue();
}
// test for e1&&e2 - include e1.T in e2
if (o1 instanceof String s && s.length()>0) {
System.out.print("done");
}
// Test for (e1 || e2).F = union(e1.F, e2.F)
if (!(o1 instanceof String s) || !(o3 instanceof Integer in)){
} else {
s.length();
i.intValue();
}
// Test for e1||e2 - include e1.F in e2
if (!(o1 instanceof String s) || s.length()>0) {
System.out.println("done");
}
// Test for e1 ? e2: e3 - include e1.T in e2
if (o1 instanceof String s ? s.length()>0 : false) {
System.out.println("done");
}
// Test for e1 ? e2 : e3 - include e1.F in e3
if (!(o1 instanceof String s) ? false : s.length()>0){
System.out.println("done");
}
// Test for (!e).T = e.F
if (!(!(o1 instanceof String s) || !(o3 instanceof Integer in))){
s.length();
i.intValue();
}
// Test for (!e).F = e.T
if (!(o1 instanceof String s)) {
} else {
s.length();
}
L1: {
if (o1 instanceof String s) {
s.length();
} else {
break L1;
}
s.length();
}
L2: {
if (!(o1 instanceof String s)) {
break L2;
} else {
s.length();
}
s.length();
}
L4: {
if (!(o1 instanceof String s)) {
break L4;
}
s.length();
}
{
while (!(o1 instanceof String s)) {
}
s.length();
}
L5: {
while (!(o1 instanceof String s)) {
}
s.length();
}
{
L6: for ( ;!(o1 instanceof String s); ) {
}
s.length();
}
{
L7: do {
} while (!(o1 instanceof String s));
s.length();
}
if (o1 instanceof String s) {
Runnable r1 = new Runnable() {
@Override
public void run() {
s.length();
}
};
r1.run();
Runnable r2 = () -> {
s.length();
};
r2.run();
String s2 = s;
}
System.out.println("BindingsTest1 complete");
}
}

View File

@ -0,0 +1,72 @@
/*
* @test /nodynamiccopyright/
* @bug 8231827
* @summary Basic tests for bindings from instanceof - tests for merging pattern variables
* @compile/fail/ref=BindingsTest1Merging.out -XDrawDiagnostics --enable-preview -source ${jdk.version} BindingsTest1Merging.java
*/
public class BindingsTest1Merging {
public static boolean Ktrue() { return true; }
public static void main(String[] args) {
Object o1 = "hello";
Integer i = 42;
Object o2 = i;
Object o3 = "there";
// Test for e1 && e2.F = intersect(e1.F, e2.F)
if (!(o1 instanceof String s) && !(o1 instanceof String s)) {
} else {
s.length();
}
// Test for (e1 || e2).T = intersect(e1.T, e2.T)
if (o1 instanceof String s || o3 instanceof String s){
System.out.println(s); // ?
}
// Test for (e1 ? e2 : e3).T contains intersect(e2.T, e3.T)
if (Ktrue() ? o2 instanceof Integer x : o2 instanceof Integer x) {
x.intValue();
}
// Test for (e1 ? e2 : e3).T contains intersect(e1.T, e3.T)
if (o1 instanceof String s ? true : o1 instanceof String s) {
s.length();
}
// Test for (e1 ? e2 : e3).T contains intersect(e1.F, e2.T)
if (!(o1 instanceof String s) ? (o1 instanceof String s) : true) {
s.length();
}
// Test for (e1 ? e2 : e3).F contains intersect(e2.F, e3.F)
if (Ktrue() ? !(o2 instanceof Integer x) : !(o2 instanceof Integer x)){
} else {
x.intValue();
}
// Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F)
if (o1 instanceof String s ? true : !(o1 instanceof String s)){
} else {
s.length();
}
// Test for (e1 ? e2 : e3).F contains intersect(e1.F, e2.F)
if (!(o1 instanceof String s) ? !(o1 instanceof String s) : true){
} else {
s.length();
}
L3: {
if ((o1 instanceof String s) || (o3 instanceof String s)) {
s.length();
} else {
break L3;
}
s.length();
}
System.out.println("BindingsTest1Merging complete");
}
}

View File

@ -0,0 +1,12 @@
BindingsTest1Merging.java:17:39: compiler.err.match.binding.exists
BindingsTest1Merging.java:24:36: compiler.err.match.binding.exists
BindingsTest1Merging.java:29:21: compiler.err.match.binding.exists
BindingsTest1Merging.java:34:36: compiler.err.match.binding.exists
BindingsTest1Merging.java:39:39: compiler.err.match.binding.exists
BindingsTest1Merging.java:44:21: compiler.err.match.binding.exists
BindingsTest1Merging.java:50:36: compiler.err.match.binding.exists
BindingsTest1Merging.java:56:39: compiler.err.match.binding.exists
BindingsTest1Merging.java:62:42: compiler.err.match.binding.exists
- compiler.note.preview.filename: BindingsTest1Merging.java
- compiler.note.preview.recompile
9 errors

View File

@ -0,0 +1,191 @@
/*
* @test /nodynamiccopyright/
* @bug 8231827
* @summary Ensure that scopes arising from conditionalExpressions are handled corrected.
* @compile/fail/ref=BindingsTest2.out -XDrawDiagnostics -XDshould-stop.at=FLOW --enable-preview -source ${jdk.version} BindingsTest2.java
*/
public class BindingsTest2 {
public static boolean Ktrue() { return true; }
public static void main(String[] args) {
Object o1 = "hello";
Integer in = 42;
Object o2 = in;
Object o3 = "there";
if (Ktrue() ? o2 instanceof Integer x : o2 instanceof String x) {
x.intValue();
}
if (Ktrue() ? o2 instanceof Integer x : true) {
x.intValue();
}
if (o1 instanceof String s ? true : true) {
s.length();
}
if (o1 instanceof String s ? true : o2 instanceof Integer s) {
s.length();
}
if (o1 instanceof String s ? true : o2 instanceof Integer i) {
s.length();
}
// Test for (e1 ? e2 : e3).T contains intersect(e1.F, e2.T)
if (!(o1 instanceof String s) ? true : true) {
s.length();
}
if (!(o1 instanceof String s) ? (o2 instanceof Integer s) : true) {
s.length();
}
if (!(o1 instanceof String s) ? (o2 instanceof Integer i) : true) {
s.length();
i.intValue();
}
if (!(o1 instanceof String s) ? (o1 instanceof String s2) : true) {
s.length();
s2.length();
}
// Test for (e1 ? e2 : e3).F contains intersect(e2.F, e3.F)
if (Ktrue() ? !(o2 instanceof Integer x) : !(o1 instanceof String x)){
} else {
x.intValue();
}
if (Ktrue() ? !(o2 instanceof Integer x) : !(o1 instanceof String s)){
} else {
x.intValue();
}
if (Ktrue() ? !(o2 instanceof Integer x) : !(o2 instanceof Integer x1)){
} else {
x.intValue();
x1.intValue();
}
if (Ktrue() ? !(o2 instanceof Integer x) : false){
} else {
x.intValue();
}
// Test for (e1 ? e2 : e3).F contains intersect(e1.T, e3.F)
if (o1 instanceof String s ? true : !(o2 instanceof Integer s)){
} else {
s.length();
}
if (o1 instanceof String s ? true : !(o2 instanceof Integer i)){
} else {
s.length();
i.intValue();
}
if (o1 instanceof String s ? true : !(o2 instanceof String s1)){
} else {
s.length();
s1.length();
}
// Test for (e1 ? e2 : e3).F contains intersect(e1.F, e2.F)
if (!(o1 instanceof String s) ? !(o1 instanceof String s1) : true){
} else {
s.length();
s1.length();
}
if (!(o1 instanceof String s) ? !(o2 instanceof Integer s) : true){
} else {
s.length();
}
if (!(o1 instanceof String s) ? !(o2 instanceof Integer i) : true){
} else {
s.length();
i.intValue();
}
// Test for e1 ? e2: e3 - include e1.T in e2
if (o1 instanceof String s ? false : s.length()>0) {
System.out.println("done");
}
if (o1 instanceof String s ? false : s.intValue!=0) {
System.out.println("done");
}
// Test for e1 ? e2 : e3 - include e1.F in e3
if (!(o1 instanceof String s) ? s.length()>0 : false){
System.out.println("done");
}
if (!(o1 instanceof String s) ? s.intValue>0 : false){
System.out.println("done");
}
{
while (!(o1 instanceof String s)) {
break;
}
s.length();
}
{
while (!(o1 instanceof String s)) {
if (false) break;
}
s.length();
}
{
while (!(o1 instanceof String s)) {
while (true);
break;
}
s.length();
}
{
for (; !(o1 instanceof String s); ) {
break;
}
s.length();
}
{
for (; !(o1 instanceof String s); ) {
if (false) break;
}
s.length();
}
{
for (; !(o1 instanceof String s); ) {
while (true);
break;
}
s.length();
}
{
do {
break;
} while (!(o1 instanceof String s));
s.length();
}
{
do {
if (false) break;
} while (!(o1 instanceof String s));
s.length();
}
{
do {
while (true);
break;
} while (!(o1 instanceof String s));
s.length();
}
}
}

View File

@ -0,0 +1,48 @@
BindingsTest2.java:16:21: compiler.err.match.binding.exists
BindingsTest2.java:17:14: compiler.err.cant.resolve.location.args: kindname.method, intValue, , , (compiler.misc.location.1: kindname.variable, x, java.lang.String)
BindingsTest2.java:20:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:24:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:26:36: compiler.err.match.binding.exists
BindingsTest2.java:27:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
BindingsTest2.java:30:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:35:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:37:39: compiler.err.match.binding.exists
BindingsTest2.java:38:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
BindingsTest2.java:41:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:42:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:45:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:46:13: compiler.err.cant.resolve.location: kindname.variable, s2, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:51:21: compiler.err.match.binding.exists
BindingsTest2.java:53:14: compiler.err.cant.resolve.location.args: kindname.method, intValue, , , (compiler.misc.location.1: kindname.variable, x, java.lang.String)
BindingsTest2.java:57:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:61:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:62:13: compiler.err.cant.resolve.location: kindname.variable, x1, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:66:13: compiler.err.cant.resolve.location: kindname.variable, x, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:70:36: compiler.err.match.binding.exists
BindingsTest2.java:72:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
BindingsTest2.java:76:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:77:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:81:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:82:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:87:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:88:13: compiler.err.cant.resolve.location: kindname.variable, s1, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:90:39: compiler.err.match.binding.exists
BindingsTest2.java:92:14: compiler.err.cant.resolve.location.args: kindname.method, length, , , (compiler.misc.location.1: kindname.variable, s, java.lang.Integer)
BindingsTest2.java:96:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:97:13: compiler.err.cant.resolve.location: kindname.variable, i, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:101:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:104:46: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:109:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:112:41: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:121:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:129:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:146:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:154:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:171:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:179:13: compiler.err.cant.resolve.location: kindname.variable, s, , , (compiler.misc.location: kindname.class, BindingsTest2, null)
BindingsTest2.java:135:17: compiler.err.unreachable.stmt
BindingsTest2.java:160:17: compiler.err.unreachable.stmt
BindingsTest2.java:185:17: compiler.err.unreachable.stmt
- compiler.note.preview.filename: BindingsTest2.java
- compiler.note.preview.recompile
45 errors

View File

@ -0,0 +1,18 @@
/*
* @test /nodynamicopyright/
* @bug 8231827
* @summary Match which involves a cast conversion
* @compile/fail/ref=CastConversionMatch.out -XDrawDiagnostics --enable-preview -source ${jdk.version} CastConversionMatch.java
*/
public class CastConversionMatch {
public static void main(String [] args) {
Object o = 42;
if (o instanceof int s) {
System.out.println("Okay");
} else {
throw new AssertionError("broken");
}
System.out.println(">Test complete");
}
}

View File

@ -0,0 +1,4 @@
CastConversionMatch.java:11:26: compiler.err.type.found.req: int, (compiler.misc.type.req.class.array)
- compiler.note.preview.filename: CastConversionMatch.java
- compiler.note.preview.recompile
1 error

View File

@ -0,0 +1,22 @@
/*
* @test /nodynamiccopyright/
* @bug 8231827
* @summary Basic pattern bindings scope test
* @compile/fail/ref=DuplicateBindingTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} DuplicateBindingTest.java
*/
public class DuplicateBindingTest {
int f;
public static void main(String[] args) {
if (args != null) {
int s;
if (args[0] instanceof String s) { // NOT OK. Redef same scope.
}
if (args[0] instanceof String f) { // OK to redef field.
}
}
}
}

View File

@ -0,0 +1,4 @@
DuplicateBindingTest.java:16:43: compiler.err.already.defined: kindname.variable, s, kindname.method, main(java.lang.String[])
- compiler.note.preview.filename: DuplicateBindingTest.java
- compiler.note.preview.recompile
1 error

View File

@ -0,0 +1,13 @@
/*
* @test /nodynamiccopyright/
* @bug 8187420 8231827
* @summary Error message mentions relevant types transposed
* @compile/fail/ref=EnsureTypesOrderTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} EnsureTypesOrderTest.java
*/
public class EnsureTypesOrderTest {
public static void main(String [] args) {
if (args instanceof String s) {
System.out.println("Broken");
}
}
}

View File

@ -0,0 +1,4 @@
EnsureTypesOrderTest.java:9:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.String[], java.lang.String)
- compiler.note.preview.filename: EnsureTypesOrderTest.java
- compiler.note.preview.recompile
1 error

View File

@ -0,0 +1,215 @@
/*
* Copyright (c) 2017, 2019, 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 8231827
* @summary All example code from "Pattern Matching for Java" document, released April 2017, adjusted to current state (no switches, etc)
* @compile --enable-preview -source ${jdk.version} ExamplesFromProposal.java
* @run main/othervm --enable-preview ExamplesFromProposal
*/
interface Node {
}
class IntNode implements Node {
int value;
IntNode(int value) {
this.value = value;
}
}
class NegNode implements Node {
Node node;
NegNode(Node node) {
this.node = node;
}
}
class MulNode implements Node {
Node left, right;
MulNode(Node left, Node right) {
this.left = left;
this.right = right;
}
}
class AddNode implements Node {
Node left, right;
AddNode(Node left, Node right) {
this.left = left;
this.right = right;
}
}
public class ExamplesFromProposal {
public static Object getSomething() {
return new Long(42);
}
public static int eval(Node n) {
if (n instanceof IntNode in) return in.value;
else if (n instanceof NegNode nn) return -eval(nn.node);
else if (n instanceof AddNode an) return eval(an.left) + eval(an.right);
else if (n instanceof MulNode mn) return eval(mn.left) * eval(mn.right);
else {
// should never happen
throw new AssertionError("broken");
}
}
public static String toString(Node n) {
if (n instanceof IntNode in) return String.valueOf(in.value);
else if (n instanceof NegNode nn) return "-"+eval(nn.node);
else if (n instanceof AddNode an) return eval(an.left) + " + " + eval(an.right);
else if (n instanceof MulNode mn) return eval(mn.left) + " * " + eval(mn.right);
else {
// should never happen
throw new AssertionError("broken");
}
}
public static Node simplify(Node n) {
if (n instanceof IntNode in) {
return n;
} else if (n instanceof NegNode nn) {
return new NegNode(simplify(nn.node));
} else if (n instanceof AddNode ad) {
n = simplify(ad.left);
if (n instanceof IntNode intn) {
if (intn.value == 0)
return simplify(ad.right);
else
return new AddNode(intn, simplify(ad.right));
} else {
return new AddNode(simplify(ad.left), simplify(ad.right));
}
} else if (n instanceof MulNode mn) {
return new MulNode(simplify(mn.left), simplify(mn.right));
} else {
//should never happen
throw new AssertionError("broken");
}
}
public static void testNode(Node n, int expected) {
if (eval(n) != expected)
throw new AssertionError("broken");
}
public static void main(String[] args) {
Object x = new Integer(42);
if (x instanceof Integer i) {
// can use i here
System.out.println(i.intValue());
}
Object obj = getSomething();
String formatted = "unknown";
if (obj instanceof Integer i) {
formatted = String.format("int %d", i);
}
else if (obj instanceof Byte b) {
formatted = String.format("byte %d", b);
}
else if (obj instanceof Long l) {
formatted = String.format("long %d", l);
}
else if (obj instanceof Double d) {
formatted = String.format("double %f", d);
}
else if (obj instanceof String s) {
formatted = String.format("String %s", s);
}
System.out.println(formatted);
if (obj instanceof Integer i) formatted = String.format("int %d", i);
else if (obj instanceof Byte b) formatted = String.format("byte %d", b);
else if (obj instanceof Long l) formatted = String.format("long %d", l);
else if (obj instanceof Double d) formatted = String.format("double %f", d);
else if (obj instanceof String s) formatted = String.format("String %s", s);
else formatted = String.format("Something else "+ obj.toString());
System.out.println(formatted);
Node zero = new IntNode(0);
Node one = new IntNode(1);
Node ft = new IntNode(42);
Node temp = new AddNode(zero,ft);
testNode(temp,42);
if (toString(simplify(temp)).equals(toString(ft)))
System.out.println("Simplify worked!");
else
throw new AssertionError("broken");
if (toString(simplify(new AddNode(zero,temp))).equals(toString(ft)))
System.out.println("Simplify worked!");
else
throw new AssertionError("broken");
temp = new AddNode(zero,ft);
temp = new AddNode(one,temp);
temp = new AddNode(zero,temp);
Node fortythree = new AddNode(one,ft);
if (toString(simplify(temp)).equals(toString(fortythree)))
System.out.println("Simplify worked!");
else
throw new AssertionError("broken");
x = "Hello";
if (x instanceof String s1) {
System.out.println(s1);
}
if (x instanceof String s1 && s1.length() > 0) {
System.out.println(s1);
}
if (x instanceof String s1) {
System.out.println(s1 + " is a string");
} else {
System.out.println("not a string");
}
if (!(x instanceof String s1)) {
System.out.println("not a string");
} else {
System.out.println(s1 + " is a string");
}
}
}

View File

@ -0,0 +1,21 @@
/*
* @test /nodynamiccopyright/
* @bug 8231827
* @summary Ensure that in type test patterns, the predicate is not trivially provable false.
* @compile/fail/ref=ImpossibleTypeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} ImpossibleTypeTest.java
*/
public class ImpossibleTypeTest {
public static void main(String[] args) {
int in = 42;
Integer i = 42;
if (i instanceof String s ) {
System.out.println("Broken");
}
if (i instanceof Undefined u ) {
System.out.println("Broken");
}
}
}

View File

@ -0,0 +1,5 @@
ImpossibleTypeTest.java:14:13: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: java.lang.Integer, java.lang.String)
ImpossibleTypeTest.java:17:26: compiler.err.cant.resolve.location: kindname.class, Undefined, , , (compiler.misc.location: kindname.class, ImpossibleTypeTest, null)
- compiler.note.preview.filename: ImpossibleTypeTest.java
- compiler.note.preview.recompile
2 errors

View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2013, 2019, 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 8231827
* @summary Ensure the LV table entries are generated for bindings
* @modules jdk.jdeps/com.sun.tools.classfile
* @compile -g --enable-preview -source ${jdk.version} LocalVariableTable.java
* @run main/othervm --enable-preview LocalVariableTable
*/
import java.io.*;
import java.lang.annotation.*;
import java.util.*;
import com.sun.tools.classfile.*;
/*
* The test checks that a LocalVariableTable attribute is generated for the
* method bodies containing patterns, and checks that the expected
* set of entries is found in the attribute.
*
* The test looks for test cases represented by nested classes whose
* name begins with "Pattern". Each such class contains a method
* with patterns, and because the test is compiled
* with -g, these methods should have a LocalVariableTable. The set of
* expected names in the LVT is provided in an annotation on the class for
* the test case.
*/
//Copied from: test/langtools/tools/javac/lambda/LocalVariableTable.java
public class LocalVariableTable {
public static void main(String... args) throws Exception {
new LocalVariableTable().run();
}
void run() throws Exception {
// the declared classes are returned in an unspecified order,
// so for neatness, sort them by name before processing them
Class<?>[] classes = getClass().getDeclaredClasses();
Arrays.sort(classes, (c1, c2) -> c1.getName().compareTo(c2.getName()));
for (Class<?> c : classes) {
if (c.getSimpleName().startsWith("Pattern"))
check(c);
}
if (errors > 0)
throw new Exception(errors + " errors found");
}
/** Check an individual test case. */
void check(Class<?> c) throws Exception {
System.err.println("Checking " + c.getSimpleName());
Expect expect = c.getAnnotation(Expect.class);
if (expect == null) {
error("@Expect not found for class " + c.getSimpleName());
return;
}
ClassFile cf = ClassFile.read(getClass().getResource(c.getName() + ".class").openStream());
Method m = getMethodByName(cf, c.getSimpleName().contains("Lambda") ? "lambda$" : "test");
if (m == null) {
error("test method not found");
return;
}
Code_attribute code = (Code_attribute) m.attributes.get(Attribute.Code);
if (code == null) {
error("Code attribute not found");
return;
}
LocalVariableTable_attribute lvt =
(LocalVariableTable_attribute) code.attributes.get(Attribute.LocalVariableTable);
if (lvt == null) {
error("LocalVariableTable attribute not found");
return;
}
Set<String> foundNames = new LinkedHashSet<>();
for (LocalVariableTable_attribute.Entry e: lvt.local_variable_table) {
foundNames.add(cf.constant_pool.getUTF8Value(e.name_index));
}
Set<String> expectNames = new LinkedHashSet<>(Arrays.asList(expect.value()));
if (!foundNames.equals(expectNames)) {
Set<String> foundOnly = new LinkedHashSet<>(foundNames);
foundOnly.removeAll(expectNames);
for (String s: foundOnly)
error("Unexpected name found: " + s);
Set<String> expectOnly = new LinkedHashSet<>(expectNames);
expectOnly.removeAll(foundNames);
for (String s: expectOnly)
error("Expected name not found: " + s);
}
}
Method getMethodByName(ClassFile cf, String name) throws ConstantPoolException {
for (Method m: cf.methods) {
if (m.getName(cf.constant_pool).startsWith(name))
return m;
}
return null;
}
/** Report an error. */
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
/**
* Annotation used to provide the set of names expected in the LVT attribute.
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Expect {
String[] value();
}
/*
* ---------- Test cases ---------------------------------------------------
*/
@Expect({ "o", "s" })
static class Pattern_Simple {
public static void test(Object o) {
if (o instanceof String s) {}
}
}
@Expect({ "s" })
static class Pattern_Lambda {
public static void test(Object o) {
if (o instanceof String s) {
Runnable r = () -> {
s.length();
};
}
}
}
}

View File

@ -0,0 +1,68 @@
/*
* @test /nodynamiccopyright/
* @bug 8231827
* @summary Basic pattern bindings scope test
* @compile/fail/ref=MatchBindingScopeTest.out -XDrawDiagnostics --enable-preview -source ${jdk.version} MatchBindingScopeTest.java
*/
public class MatchBindingScopeTest {
static Integer i = 42;
static String s = "Hello";
static Object o1 = s;
static Object o2 = i;
public static void main(String[] args) {
if (o1 instanceof String j && j.length() == 5) { // OK
System.out.println(j); // OK
} else {
System.out.println(j); // NOT OK
}
// NOT OK, name reused.
if (o1 instanceof String j && o2 instanceof Integer j) {
}
if (o1 instanceof String j && j.length() == 5 && o2 instanceof Integer k && k == 42) { // OK
System.out.println(j); // OK
System.out.println(k); // OK
} else {
System.out.println(j); // NOT OK
System.out.println(k); // NOT OK
}
if (o1 instanceof String j || j.length() == 5) { // NOT OK
System.out.println(j); // NOT OK
}
if (o1 instanceof String j || o2 instanceof Integer j) { // NOT OK, types differ
System.out.println(j);
} else {
System.out.println(j); // NOT OK.
}
while (o1 instanceof String j && j.length() == 5) { // OK
System.out.println(j); // OK
}
while (o1 instanceof String j || true) {
System.out.println(j); // Not OK
}
for (; o1 instanceof String j; j.length()) { // OK
System.out.println(j); // OK
}
for (; o1 instanceof String j || true; j.length()) { // NOT OK
System.out.println(j); // Not OK
}
int x = o1 instanceof String j ?
j.length() : // OK.
j.length(); // NOT OK.
x = !(o1 instanceof String j) ?
j.length() : // NOT OK.
j.length(); // OK.
}
}

View File

@ -0,0 +1,16 @@
MatchBindingScopeTest.java:19:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:23:36: compiler.err.match.binding.exists
MatchBindingScopeTest.java:30:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:31:32: compiler.err.cant.resolve.location: kindname.variable, k, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:34:39: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:35:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:38:36: compiler.err.match.binding.exists
MatchBindingScopeTest.java:41:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:49:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:56:48: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:57:32: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:62:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
MatchBindingScopeTest.java:65:23: compiler.err.cant.resolve.location: kindname.variable, j, , , (compiler.misc.location: kindname.class, MatchBindingScopeTest, null)
- compiler.note.preview.filename: MatchBindingScopeTest.java
- compiler.note.preview.recompile
13 errors

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2017, 2019, 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 8231827
* @summary Testing pattern matching against the null constant
* @compile --enable-preview -source ${jdk.version} NullsInPatterns.java
* @run main/othervm --enable-preview NullsInPatterns
*/
import java.util.List;
public class NullsInPatterns {
public static void main(String[] args) {
if (null instanceof List t) {
throw new AssertionError("broken");
} else {
System.out.println("null does not match List type pattern");
}
//reifiable types not allowed in type test patterns in instanceof:
// if (null instanceof List<Integer> l) {
// throw new AssertionError("broken");
// } else {
// System.out.println("null does not match List<Integer> type pattern");
// }
if (null instanceof List<?> l) {
throw new AssertionError("broken");
} else {
System.out.println("null does not match List<?> type pattern");
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2017, 2019, 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 8231827
* @summary Check proper positions.
* @build PatternMatchPosTest
* @compile/ref=PatternMatchPosTest.out -processor PatternMatchPosTest -Xlint:unchecked -XDrawDiagnostics --enable-preview -source ${jdk.version} PatternMatchPosTestData.java
*/
import java.io.IOException;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import com.sun.source.tree.IfTree;
import com.sun.source.tree.Tree;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;
import javax.tools.Diagnostic;
@SupportedAnnotationTypes("*")
public class PatternMatchPosTest extends AbstractProcessor {
int round;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (round++ != 0)
return false;
try {
TypeElement data = processingEnv.getElementUtils().getTypeElement("PatternMatchPosTestData");
Trees trees = Trees.instance(processingEnv);
SourcePositions sp = trees.getSourcePositions();
TreePath dataPath = trees.getPath(data);
String text = dataPath.getCompilationUnit().getSourceFile().getCharContent(true).toString();
new TreeScanner<Void, Void>() {
boolean print;
@Override
public Void visitIf(IfTree node, Void p) {
boolean prevPrint = print;
try {
print = true;
scan(node.getCondition(), p);
} finally {
print = prevPrint;
}
scan(node.getThenStatement(), p);
scan(node.getElseStatement(), p);
return null;
}
@Override
public Void scan(Tree tree, Void p) {
if (tree == null)
return null;
if (print) {
int start = (int) sp.getStartPosition(dataPath.getCompilationUnit(), tree);
int end = (int) sp.getEndPosition(dataPath.getCompilationUnit(), tree);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
text.substring(start, end));
}
return super.scan(tree, p);
}
}.scan(dataPath.getLeaf(), null);
return false;
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}

View File

@ -0,0 +1,14 @@
- compiler.note.proc.messager: (o instanceof String s)
- compiler.note.proc.messager: o instanceof String s
- compiler.note.proc.messager: o
- compiler.note.proc.messager: String s
- compiler.note.proc.messager: String
- compiler.note.proc.messager: (o instanceof java.lang.String s)
- compiler.note.proc.messager: o instanceof java.lang.String s
- compiler.note.proc.messager: o
- compiler.note.proc.messager: java.lang.String s
- compiler.note.proc.messager: java.lang.String
- compiler.note.proc.messager: java.lang
- compiler.note.proc.messager: java
- compiler.note.preview.filename: PatternMatchPosTestData.java
- compiler.note.preview.recompile

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2017, 2019, 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.
*/
class PatternMatchPosTestData {
void data(Object o) {
if (o instanceof String s) { }
if (o instanceof java.lang.String s) { }
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2017, 2019, 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 8231827
* @summary Basic pattern test
* @compile --enable-preview -source ${jdk.version} PatternTypeTest2.java
* @run main/othervm --enable-preview PatternTypeTest2
*/
public class PatternTypeTest2 {
public static void main(String[] args) {
Integer i = 42;
String s = "Hello";
Object o = i;
if (o instanceof Integer j) {
System.out.println("It's an Integer");
} else {
throw new AssertionError("Broken");
}
}
}

Some files were not shown because too many files have changed in this diff Show More