diff --git a/agent/src/os/bsd/MacosxDebuggerLocal.m b/agent/src/os/bsd/MacosxDebuggerLocal.m index 0f0ff4e5da4475cb7ceebca6d49c179c658be481..adc10f31a7e25a70065728c992849f2101858ef6 100644 --- a/agent/src/os/bsd/MacosxDebuggerLocal.m +++ b/agent/src/os/bsd/MacosxDebuggerLocal.m @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2015, 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 @@ -26,7 +26,7 @@ #import #import -#include +#include #import #import diff --git a/agent/src/os/bsd/Makefile b/agent/src/os/bsd/Makefile index ce3e2c4c32c411e63f6d90c8b15555a1c5379108..af22b597b8e007ee80fa4d1f44ed2c82b259eab5 100644 --- a/agent/src/os/bsd/Makefile +++ b/agent/src/os/bsd/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2015, 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,9 +50,9 @@ SOURCES = symtab.c \ ps_core.c OBJS = $(SOURCES:.c=.o) OBJSPLUS = MacosxDebuggerLocal.o sadis.o $(OBJS) -EXTINCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers -I. +EXTINCLUDE = -I. EXTCFLAGS = -m64 -D__APPLE__ -framework JavaNativeFoundation -FOUNDATIONFLAGS = -framework Foundation -F/System/Library/Frameworks/JavaVM.framework/Frameworks -framework JavaNativeFoundation -framework Security -framework CoreFoundation +FOUNDATIONFLAGS = -framework Foundation -framework JavaNativeFoundation -framework Security -framework CoreFoundation LIBSA = $(ARCH)/libsaproc.dylib endif # Darwin diff --git a/make/bsd/makefiles/saproc.make b/make/bsd/makefiles/saproc.make index 8cb38dbca47df64bf28728cc0a7ae6ca966969ab..831ef3e0405545f67d2051425756a8eb06cb5ecb 100644 --- a/make/bsd/makefiles/saproc.make +++ b/make/bsd/makefiles/saproc.make @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2015, 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 @@ -64,9 +64,22 @@ ifeq ($(OS_VENDOR), FreeBSD) else ifeq ($(OS_VENDOR), Darwin) SASRCFILES = $(DARWIN_NON_STUB_SASRCFILES) - SALIBS = -g -framework Foundation -F/System/Library/Frameworks/JavaVM.framework/Frameworks -framework JavaNativeFoundation -framework Security -framework CoreFoundation + SALIBS = -g \ + -framework Foundation \ + -framework JavaNativeFoundation \ + -framework Security \ + -framework CoreFoundation #objc compiler blows up on -march=i586, perhaps it should not be included in the macosx intel 32-bit C++ compiles? SAARCH = $(subst -march=i586,,$(ARCHFLAG)) + + # This is needed to locate JavaNativeFoundation.framework + # JDK 8 doesn't have SYSROOT_CFLAGS, so we'll cobble it together here + SA_SYSROOT_FLAGS= + ifneq ($(SDKPATH),) + SA_SYSROOT_FLAGS += -isysroot "$(SDKPATH)" -iframework"$(SDKPATH)/System/Library/Frameworks" + endif + # always needed, even if SDKPATH is empty + SA_SYSROOT_FLAGS += -F"$(SDKPATH)/System/Library/Frameworks/JavaVM.framework/Frameworks" else SASRCFILES = $(SASRCDIR)/StubDebuggerLocal.c SALIBS = @@ -100,14 +113,8 @@ SA_LFLAGS = $(MAPFLAG:FILENAME=$(SAMAPFILE)) endif SA_LFLAGS += $(LDFLAGS_HASH_STYLE) -ifeq ($(OS_VENDOR), Darwin) - BOOT_JAVA_INCLUDES = -I$(BOOT_JAVA_HOME)/include \ - -I$(BOOT_JAVA_HOME)/include/$(shell uname -s | tr "[:upper:]" "[:lower:]") \ - -I/System/Library/Frameworks/JavaVM.framework/Headers -else - BOOT_JAVA_INCLUDES = -I$(BOOT_JAVA_HOME)/include \ - -I$(BOOT_JAVA_HOME)/include/$(shell uname -s | tr "[:upper:]" "[:lower:]") -endif +BOOT_JAVA_INCLUDES = -I$(BOOT_JAVA_HOME)/include \ + -I$(BOOT_JAVA_HOME)/include/$(shell uname -s | tr "[:upper:]" "[:lower:]") $(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) $(QUIETLY) if [ "$(BOOT_JAVA_HOME)" = "" ]; then \ @@ -116,6 +123,7 @@ $(LIBSAPROC): $(SASRCFILES) $(SAMAPFILE) fi @echo Making SA debugger back-end... $(QUIETLY) $(CC) -D$(BUILDARCH) -D_GNU_SOURCE \ + $(SA_SYSROOT_FLAGS) \ $(SYMFLAG) $(SAARCH) $(SHARED_FLAG) $(PICFLAG) \ -I$(SASRCDIR) \ -I$(GENERATED) \ diff --git a/make/hotspot_version b/make/hotspot_version index 81063f087ea0f2ee233c5d194235bdc0e204daeb..bd27c28192596d4c1e85536d7311f2055c7fe45c 100644 --- a/make/hotspot_version +++ b/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2015 HS_MAJOR_VER=25 HS_MINOR_VER=60 -HS_BUILD_NUMBER=01 +HS_BUILD_NUMBER=02 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff --git a/src/share/vm/opto/chaitin.cpp b/src/share/vm/opto/chaitin.cpp index e54d5b674543dac91bab436ff0dd4b39165c7bff..20526bf2734835c15e3337737419386d91437a4a 100644 --- a/src/share/vm/opto/chaitin.cpp +++ b/src/share/vm/opto/chaitin.cpp @@ -575,6 +575,9 @@ void PhaseChaitin::Register_Allocate() { // Peephole remove copies post_allocate_copy_removal(); + // Merge multidefs if multiple defs representing the same value are used in a single block. + merge_multidefs(); + #ifdef ASSERT // Veify the graph after RA. verify(&live_arena); diff --git a/src/share/vm/opto/chaitin.hpp b/src/share/vm/opto/chaitin.hpp index b188cc9bbf8460eebc5a80496124e67da5263c32..de6d443cd3036b20c5e6b1491e5d579597bcc6fc 100644 --- a/src/share/vm/opto/chaitin.hpp +++ b/src/share/vm/opto/chaitin.hpp @@ -578,6 +578,32 @@ private: // Extend the node to LRG mapping void add_reference( const Node *node, const Node *old_node); + // Record the first use of a def in the block for a register. + class RegDefUse { + Node* _def; + Node* _first_use; + public: + RegDefUse() : _def(NULL), _first_use(NULL) { } + Node* def() const { return _def; } + Node* first_use() const { return _first_use; } + + void update(Node* def, Node* use) { + if (_def != def) { + _def = def; + _first_use = use; + } + } + void clear() { + _def = NULL; + _first_use = NULL; + } + }; + typedef GrowableArray RegToDefUseMap; + int possibly_merge_multidef(Node *n, uint k, Block *block, RegToDefUseMap& reg2defuse); + + // Merge nodes that are a part of a multidef lrg and produce the same value within a block. + void merge_multidefs(); + private: static int _final_loads, _final_stores, _final_copies, _final_memoves; diff --git a/src/share/vm/opto/lcm.cpp b/src/share/vm/opto/lcm.cpp index 8398bb3d2548f508191105592c5ff1209e587e77..7aedc71b20fec4bed5f3f521bb85d74e66a3aa16 100644 --- a/src/share/vm/opto/lcm.cpp +++ b/src/share/vm/opto/lcm.cpp @@ -437,8 +437,15 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo for (DUIterator_Last i2min, i2 = old_tst->last_outs(i2min); i2 >= i2min; --i2) old_tst->last_out(i2)->set_req(0, nul_chk); // Clean-up any dead code - for (uint i3 = 0; i3 < old_tst->req(); i3++) + for (uint i3 = 0; i3 < old_tst->req(); i3++) { + Node* in = old_tst->in(i3); old_tst->set_req(i3, NULL); + if (in->outcnt() == 0) { + // Remove dead input node + in->disconnect_inputs(NULL, C); + block->find_remove(in); + } + } latency_from_uses(nul_chk); latency_from_uses(best); diff --git a/src/share/vm/opto/machnode.hpp b/src/share/vm/opto/machnode.hpp index 8c55a8d234e3c5409b874b2c8bebd17b2ffd12be..5f8757c9adf9881833b0d1f1d3c9d2c928d70826 100644 --- a/src/share/vm/opto/machnode.hpp +++ b/src/share/vm/opto/machnode.hpp @@ -558,6 +558,29 @@ public: #endif }; +// MachMergeNode is similar to a PhiNode in a sense it merges multiple values, +// however it doesn't have a control input and is more like a MergeMem. +// It is inserted after the register allocation is done to ensure that nodes use single +// definition of a multidef lrg in a block. +class MachMergeNode : public MachIdealNode { +public: + MachMergeNode(Node *n1) { + init_class_id(Class_MachMerge); + add_req(NULL); + add_req(n1); + } + virtual const RegMask &out_RegMask() const { return in(1)->out_RegMask(); } + virtual const RegMask &in_RegMask(uint idx) const { return in(1)->in_RegMask(idx); } + virtual const class Type *bottom_type() const { return in(1)->bottom_type(); } + virtual uint ideal_reg() const { return bottom_type()->ideal_reg(); } + virtual uint oper_input_base() const { return 1; } + virtual void emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { } + virtual uint size(PhaseRegAlloc *ra_) const { return 0; } +#ifndef PRODUCT + virtual const char *Name() const { return "MachMerge"; } +#endif +}; + //------------------------------MachBranchNode-------------------------------- // Abstract machine branch Node class MachBranchNode : public MachIdealNode { diff --git a/src/share/vm/opto/node.hpp b/src/share/vm/opto/node.hpp index 0b51d04875d15b1cc739f59b27b519f77ba164ca..fed9f345f0b26b5727938355bea28cca699571ac 100644 --- a/src/share/vm/opto/node.hpp +++ b/src/share/vm/opto/node.hpp @@ -98,6 +98,7 @@ class MachReturnNode; class MachSafePointNode; class MachSpillCopyNode; class MachTempNode; +class MachMergeNode; class Matcher; class MemBarNode; class MemBarStoreStoreNode; @@ -591,6 +592,7 @@ public: DEFINE_CLASS_ID(MachTemp, Mach, 3) DEFINE_CLASS_ID(MachConstantBase, Mach, 4) DEFINE_CLASS_ID(MachConstant, Mach, 5) + DEFINE_CLASS_ID(MachMerge, Mach, 6) DEFINE_CLASS_ID(Type, Node, 2) DEFINE_CLASS_ID(Phi, Type, 0) @@ -761,6 +763,7 @@ public: DEFINE_CLASS_QUERY(MachSafePoint) DEFINE_CLASS_QUERY(MachSpillCopy) DEFINE_CLASS_QUERY(MachTemp) + DEFINE_CLASS_QUERY(MachMerge) DEFINE_CLASS_QUERY(Mem) DEFINE_CLASS_QUERY(MemBar) DEFINE_CLASS_QUERY(MemBarStoreStore) diff --git a/src/share/vm/opto/phase.cpp b/src/share/vm/opto/phase.cpp index a80840d7f4352b8348c58a3ab39edf692921f49d..de9f49e6d146559c92a4ac110a271f24e1d4fa0c 100644 --- a/src/share/vm/opto/phase.cpp +++ b/src/share/vm/opto/phase.cpp @@ -74,6 +74,7 @@ elapsedTimer Phase::_t_buildIFGphysical; elapsedTimer Phase::_t_computeLive; elapsedTimer Phase::_t_regAllocSplit; elapsedTimer Phase::_t_postAllocCopyRemoval; +elapsedTimer Phase::_t_mergeMultidefs; elapsedTimer Phase::_t_fixupSpills; // Subtimers for _t_output @@ -136,11 +137,12 @@ void Phase::print_timers() { tty->print_cr (" computeLive : %3.3f sec", Phase::_t_computeLive.seconds()); tty->print_cr (" regAllocSplit : %3.3f sec", Phase::_t_regAllocSplit.seconds()); tty->print_cr (" postAllocCopyRemoval: %3.3f sec", Phase::_t_postAllocCopyRemoval.seconds()); + tty->print_cr (" mergeMultidefs: %3.3f sec", Phase::_t_mergeMultidefs.seconds()); tty->print_cr (" fixupSpills : %3.3f sec", Phase::_t_fixupSpills.seconds()); double regalloc_subtotal = Phase::_t_ctorChaitin.seconds() + Phase::_t_buildIFGphysical.seconds() + Phase::_t_computeLive.seconds() + Phase::_t_regAllocSplit.seconds() + Phase::_t_fixupSpills.seconds() + - Phase::_t_postAllocCopyRemoval.seconds(); + Phase::_t_postAllocCopyRemoval.seconds() + Phase::_t_mergeMultidefs.seconds(); double percent_of_regalloc = ((regalloc_subtotal == 0.0) ? 0.0 : (regalloc_subtotal / Phase::_t_registerAllocation.seconds() * 100.0)); tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", regalloc_subtotal, percent_of_regalloc); } diff --git a/src/share/vm/opto/phase.hpp b/src/share/vm/opto/phase.hpp index 08e9575640ef105a423abdfacf64f965d459f210..f46c9a963bc94c5b165553bc84f2fde558b6eee3 100644 --- a/src/share/vm/opto/phase.hpp +++ b/src/share/vm/opto/phase.hpp @@ -109,6 +109,7 @@ protected: static elapsedTimer _t_computeLive; static elapsedTimer _t_regAllocSplit; static elapsedTimer _t_postAllocCopyRemoval; + static elapsedTimer _t_mergeMultidefs; static elapsedTimer _t_fixupSpills; // Subtimers for _t_output diff --git a/src/share/vm/opto/postaloc.cpp b/src/share/vm/opto/postaloc.cpp index 86078979af1235b4f9d9efc94c0b73084ce64b75..f245776464fc6f850d58d2d2bba1ba17aac033ce 100644 --- a/src/share/vm/opto/postaloc.cpp +++ b/src/share/vm/opto/postaloc.cpp @@ -263,20 +263,6 @@ int PhaseChaitin::elide_copy( Node *n, int k, Block *current_block, Node_List &v // intermediate copies might be illegal, i.e., value is stored down to stack // then reloaded BUT survives in a register the whole way. Node *val = skip_copies(n->in(k)); - - if (val == x && nk_idx != 0 && - regnd[nk_reg] != NULL && regnd[nk_reg] != x && - _lrg_map.live_range_id(x) == _lrg_map.live_range_id(regnd[nk_reg])) { - // When rematerialzing nodes and stretching lifetimes, the - // allocator will reuse the original def for multidef LRG instead - // of the current reaching def because it can't know it's safe to - // do so. After allocation completes if they are in the same LRG - // then it should use the current reaching def instead. - n->set_req(k, regnd[nk_reg]); - blk_adjust += yank_if_dead(val, current_block, &value, ®nd); - val = skip_copies(n->in(k)); - } - if (val == x) return blk_adjust; // No progress? int n_regs = RegMask::num_registers(val->ideal_reg()); @@ -382,6 +368,94 @@ bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Node* n, return false; } +// The algorithms works as follows: +// We traverse the block top to bottom. possibly_merge_multidef() is invoked for every input edge k +// of the instruction n. We check to see if the input is a multidef lrg. If it is, we record the fact that we've +// seen a definition (coming as an input) and add that fact to the reg2defuse array. The array maps registers to their +// current reaching definitions (we track only multidefs though). With each definition we also associate the first +// instruction we saw use it. If we encounter the situation when we observe an def (an input) that is a part of the +// same lrg but is different from the previous seen def we merge the two with a MachMerge node and substitute +// all the uses that we've seen so far to use the merge. After that we keep replacing the new defs in the same lrg +// as they get encountered with the merge node and keep adding these defs to the merge inputs. +void PhaseChaitin::merge_multidefs() { + NOT_PRODUCT( Compile::TracePhase t3("mergeMultidefs", &_t_mergeMultidefs, TimeCompiler); ) + ResourceMark rm; + // Keep track of the defs seen in registers and collect their uses in the block. + RegToDefUseMap reg2defuse(_max_reg, _max_reg, RegDefUse()); + for (uint i = 0; i < _cfg.number_of_blocks(); i++) { + Block* block = _cfg.get_block(i); + for (uint j = 1; j < block->number_of_nodes(); j++) { + Node* n = block->get_node(j); + if (n->is_Phi()) continue; + for (uint k = 1; k < n->req(); k++) { + j += possibly_merge_multidef(n, k, block, reg2defuse); + } + // Null out the value produced by the instruction itself, since we're only interested in defs + // implicitly defined by the uses. We are actually interested in tracking only redefinitions + // of the multidef lrgs in the same register. For that matter it's enough to track changes in + // the base register only and ignore other effects of multi-register lrgs and fat projections. + // It is also ok to ignore defs coming from singledefs. After an implicit overwrite by one of + // those our register is guaranteed to be used by another lrg and we won't attempt to merge it. + uint lrg = _lrg_map.live_range_id(n); + if (lrg > 0 && lrgs(lrg).is_multidef()) { + OptoReg::Name reg = lrgs(lrg).reg(); + reg2defuse.at(reg).clear(); + } + } + // Clear reg->def->use tracking for the next block + for (int j = 0; j < reg2defuse.length(); j++) { + reg2defuse.at(j).clear(); + } + } +} + +int PhaseChaitin::possibly_merge_multidef(Node *n, uint k, Block *block, RegToDefUseMap& reg2defuse) { + int blk_adjust = 0; + + uint lrg = _lrg_map.live_range_id(n->in(k)); + if (lrg > 0 && lrgs(lrg).is_multidef()) { + OptoReg::Name reg = lrgs(lrg).reg(); + + Node* def = reg2defuse.at(reg).def(); + if (def != NULL && lrg == _lrg_map.live_range_id(def) && def != n->in(k)) { + // Same lrg but different node, we have to merge. + MachMergeNode* merge; + if (def->is_MachMerge()) { // is it already a merge? + merge = def->as_MachMerge(); + } else { + merge = new (C) MachMergeNode(def); + + // Insert the merge node into the block before the first use. + uint use_index = block->find_node(reg2defuse.at(reg).first_use()); + block->insert_node(merge, use_index++); + + // Let the allocator know about the new node, use the same lrg + _lrg_map.extend(merge->_idx, lrg); + blk_adjust++; + + // Fixup all the uses (there is at least one) that happened between the first + // use and before the current one. + for (; use_index < block->number_of_nodes(); use_index++) { + Node* use = block->get_node(use_index); + if (use == n) { + break; + } + use->replace_edge(def, merge); + } + } + if (merge->find_edge(n->in(k)) == -1) { + merge->add_req(n->in(k)); + } + n->set_req(k, merge); + } + + // update the uses + reg2defuse.at(reg).update(n->in(k), n); + } + + return blk_adjust; +} + //------------------------------post_allocate_copy_removal--------------------- // Post-Allocation peephole copy removal. We do this in 1 pass over the diff --git a/src/share/vm/opto/stringopts.cpp b/src/share/vm/opto/stringopts.cpp index b00bb881faab8aaa777defc427a3228b56eb95df..90655d80673ef30c170ba9f6c5a5c7c1cec2c9f9 100644 --- a/src/share/vm/opto/stringopts.cpp +++ b/src/share/vm/opto/stringopts.cpp @@ -1507,10 +1507,12 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { } case StringConcat::StringMode: { const Type* type = kit.gvn().type(arg); + Node* count = NULL; if (type == TypePtr::NULL_PTR) { // replace the argument with the null checked version arg = null_string; sc->set_argument(argi, arg); + count = kit.load_String_length(kit.control(), arg); } else if (!type->higher_equal(TypeInstPtr::NOTNULL)) { // s = s != null ? s : "null"; // length = length + (s.count - s.offset); @@ -1533,10 +1535,13 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) { // replace the argument with the null checked version arg = phi; sc->set_argument(argi, arg); + count = kit.load_String_length(kit.control(), arg); + } else { + // A corresponding nullcheck will be connected during IGVN MemNode::Ideal_common_DU_postCCP + // kit.control might be a different test, that can be hoisted above the actual nullcheck + // in case, that the control input is not null, Ideal_common_DU_postCCP will not look for a nullcheck. + count = kit.load_String_length(NULL, arg); } - - Node* count = kit.load_String_length(kit.control(), arg); - length = __ AddI(length, count); string_sizes->init_req(argi, NULL); break; diff --git a/test/compiler/stringopts/TestOptimizeStringConcat.java b/test/compiler/stringopts/TestOptimizeStringConcat.java new file mode 100644 index 0000000000000000000000000000000000000000..771ffb0bd68bd0398e50a1af7f82c96671a582af --- /dev/null +++ b/test/compiler/stringopts/TestOptimizeStringConcat.java @@ -0,0 +1,89 @@ +/* + * Copyright 2015 SAP AG. 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 8068909 + * @key regression + * @summary test that string optimizations produce code, that doesn't lead to a crash. + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestOptimizeStringConcat + * @author axel.siebenborn@sap.com + */ +public class TestOptimizeStringConcat { + + static boolean checkArgumentSyntax(String value, String allowedchars, String notallowedchars, String logmsg) { + String rc = null; + + int maxchar = 99999; + int minchar = 1; + if ((allowedchars != null && notallowedchars != null) || minchar > maxchar) { + rc = "internal error"; + } else { + if (value == null) { + rc = "the value null is not allowed, it is missing"; + } else if (value != null && minchar > 0 && value.trim().equals("")) { + rc = "the value must not be empty"; + } else if (value != null) { + if (value.length() < minchar || value.length() > maxchar) { + if (rc == null) { + rc = "the value length must be between +minchar+ and +maxchar"; + } + } + char[] _value = value.toCharArray(); + boolean dotfound = false; + int i = 1; + if (_value[i] == '.' && !dotfound) { + dotfound = true; + } else if (allowedchars != null && allowedchars.indexOf(_value[i]) == -1) { + if (rc == null) { + rc = "the value contains an illegal character: '" + _value[i] + "', only following characters are allowed: '+allowedchars+'"; + } else { + rc += " / the value contains an illegal character: '" + _value[i] + "', only following characters are allowed: '+allowedchars+'"; + } + } else if (notallowedchars != null && notallowedchars.indexOf(_value[i]) != -1) { + if (rc == null) { + rc = "the value contains an illegal character: '" + _value[i] + "', following characters are not allowed '+notallowedchars+'"; + } else { + rc += " / the value contains an illegal character: '" + _value[i] + "', following characters are not allowed '+notallowedchars+'"; + } + } + } + } + + if (rc != null) { + System.out.println(logmsg + " ==> " + rc); + return false; + } + return true; + } + + public static void main(String[] args) { + boolean failed = false; + for (int i = 0; i < 10000; i++) { + failed |= !checkArgumentSyntax("theName", null, "\"<&", "Error consistencyCheck: name in component definition"); + failed |= !checkArgumentSyntax(null, null, "\"<&", "Error consistencyCheck: name in component definition"); + failed |= !checkArgumentSyntax("42", "0123456789.", null, "Error consistencyCheck: counter in component definition"); + } + System.out.println(failed); + } +}