mirror of
https://github.com/darlinghq/darling-openjdk.git
synced 2024-11-30 15:50:29 +00:00
Merge
This commit is contained in:
commit
a5b3bfd8a2
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
%{
|
||||
|
@ -123,6 +123,7 @@ void ADLParser::parse() {
|
||||
parse_err(SEMERR, "Did not declare 'register' definitions");
|
||||
}
|
||||
regBlock->addSpillRegClass();
|
||||
regBlock->addDynamicRegClass();
|
||||
|
||||
// Done with parsing, check consistency.
|
||||
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,7 +152,6 @@ public class MutableCallSite extends CallSite {
|
||||
* @see #getTarget
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(this.target, newTarget);
|
||||
setTargetNormal(newTarget);
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,6 @@ public class VolatileCallSite extends CallSite {
|
||||
* @see #getTarget
|
||||
*/
|
||||
@Override public void setTarget(MethodHandle newTarget) {
|
||||
checkTargetChange(getTargetVolatile(), newTarget);
|
||||
setTargetVolatile(newTarget);
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,8 @@ public @interface PreviewFeature {
|
||||
public boolean essentialAPI() default false;
|
||||
|
||||
public enum Feature {
|
||||
TEXT_BLOCKS;
|
||||
PATTERN_MATCHING_IN_INSTANCEOF,
|
||||
TEXT_BLOCKS,
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 {}
|
@ -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}.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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}.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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'
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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); }
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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) {}
|
||||
}
|
||||
}
|
@ -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 = "";
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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> {}
|
||||
}
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
30
test/langtools/tools/javac/patterns/BindingsExistTest.java
Normal file
30
test/langtools/tools/javac/patterns/BindingsExistTest.java
Normal 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 = "";
|
||||
}
|
||||
}
|
@ -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
|
166
test/langtools/tools/javac/patterns/BindingsTest1.java
Normal file
166
test/langtools/tools/javac/patterns/BindingsTest1.java
Normal 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");
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
12
test/langtools/tools/javac/patterns/BindingsTest1Merging.out
Normal file
12
test/langtools/tools/javac/patterns/BindingsTest1Merging.out
Normal 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
|
191
test/langtools/tools/javac/patterns/BindingsTest2.java
Normal file
191
test/langtools/tools/javac/patterns/BindingsTest2.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
48
test/langtools/tools/javac/patterns/BindingsTest2.out
Normal file
48
test/langtools/tools/javac/patterns/BindingsTest2.out
Normal 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
|
18
test/langtools/tools/javac/patterns/CastConversionMatch.java
Normal file
18
test/langtools/tools/javac/patterns/CastConversionMatch.java
Normal 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");
|
||||
}
|
||||
}
|
@ -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
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
215
test/langtools/tools/javac/patterns/ExamplesFromProposal.java
Normal file
215
test/langtools/tools/javac/patterns/ExamplesFromProposal.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
21
test/langtools/tools/javac/patterns/ImpossibleTypeTest.java
Normal file
21
test/langtools/tools/javac/patterns/ImpossibleTypeTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
165
test/langtools/tools/javac/patterns/LocalVariableTable.java
Normal file
165
test/langtools/tools/javac/patterns/LocalVariableTable.java
Normal 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();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
@ -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
|
53
test/langtools/tools/javac/patterns/NullsInPatterns.java
Normal file
53
test/langtools/tools/javac/patterns/NullsInPatterns.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
105
test/langtools/tools/javac/patterns/PatternMatchPosTest.java
Normal file
105
test/langtools/tools/javac/patterns/PatternMatchPosTest.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
14
test/langtools/tools/javac/patterns/PatternMatchPosTest.out
Normal file
14
test/langtools/tools/javac/patterns/PatternMatchPosTest.out
Normal 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
|
@ -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) { }
|
||||
}
|
||||
}
|
45
test/langtools/tools/javac/patterns/PatternTypeTest2.java
Normal file
45
test/langtools/tools/javac/patterns/PatternTypeTest2.java
Normal 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
Loading…
Reference in New Issue
Block a user