Path: blob/master/src/hotspot/share/compiler/methodMatcher.cpp
40930 views
/*1* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "classfile/symbolTable.hpp"26#include "classfile/vmSymbols.hpp"27#include "compiler/compilerOracle.hpp"28#include "compiler/methodMatcher.hpp"29#include "memory/oopFactory.hpp"30#include "memory/resourceArea.hpp"31#include "oops/method.hpp"32#include "oops/oop.inline.hpp"3334// The JVM specification defines the allowed characters.35// Tokens that are disallowed by the JVM specification can have36// a meaning to the parser so we need to include them here.37// The parser does not enforce all rules of the JVMS - a successful parse38// does not mean that it is an allowed name. Illegal names will39// be ignored since they never can match a class or method.40//41// '\0' and 0xf0-0xff are disallowed in constant string values42// 0x20 ' ', 0x09 '\t' and, 0x2c ',' are used in the matching43// 0x5b '[' and 0x5d ']' can not be used because of the matcher44// 0x28 '(' and 0x29 ')' are used for the signature45// 0x2e '.' is always replaced before the matching46// 0x2f '/' is only used in the class name as package separator4748#define RANGEBASE "\x1\x2\x3\x4\x5\x6\x7\x8\xa\xb\xc\xd\xe\xf" \49"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \50"\x21\x22\x23\x24\x25\x26\x27\x2a\x2b\x2c\x2d" \51"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \52"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \53"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5c\x5e\x5f" \54"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \55"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f" \56"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" \57"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" \58"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" \59"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" \60"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" \61"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf" \62"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"6364#define RANGE0 "[*" RANGEBASE "]"65#define RANGESLASH "[*" RANGEBASE "/]"6667MethodMatcher::MethodMatcher():68_class_name(NULL)69, _method_name(NULL)70, _signature(NULL)71, _class_mode(Exact)72, _method_mode(Exact) {73}7475MethodMatcher::~MethodMatcher() {76if (_class_name != NULL) {77_class_name->decrement_refcount();78}79if (_method_name != NULL) {80_method_name->decrement_refcount();81}82if (_signature != NULL) {83_signature->decrement_refcount();84}85}8687void MethodMatcher::init(Symbol* class_name, Mode class_mode,88Symbol* method_name, Mode method_mode,89Symbol* signature) {90_class_mode = class_mode;91_method_mode = method_mode;92_class_name = class_name;93_method_name = method_name;94_signature = signature;95}9697bool MethodMatcher::canonicalize(char * line, const char *& error_msg) {98char* colon = strstr(line, "::");99bool have_colon = (colon != NULL);100if (have_colon) {101// Don't allow multiple '::'102if (colon[2] != '\0') {103if (strstr(colon+2, "::")) {104error_msg = "Method pattern only allows one '::' allowed";105return false;106}107}108109char* pos = line;110if (pos != NULL) {111for (char* lp = pos + 1; *lp != '\0'; lp++) {112if (*lp == '(') {113break;114}115116if (*lp == '/') {117error_msg = "Method pattern uses '/' together with '::'";118return false;119}120}121}122} else {123// Don't allow mixed package separators124char* pos = strchr(line, '.');125bool in_signature = false;126if (pos != NULL) {127for (char* lp = pos + 1; *lp != '\0'; lp++) {128if (*lp == '(') {129in_signature = true;130}131132// After any comma the method pattern has ended133if (*lp == ',') {134break;135}136137if (!in_signature && (*lp == '/')) {138error_msg = "Method pattern uses mixed '/' and '.' package separators";139return false;140}141142if (*lp == '.') {143error_msg = "Method pattern uses multiple '.' in pattern";144return false;145}146}147}148}149150for (char* lp = line; *lp != '\0'; lp++) {151// Allow '.' to separate the class name from the method name.152// This is the preferred spelling of methods:153// exclude java/lang/String.indexOf(I)I154// Allow ',' for spaces (eases command line quoting).155// exclude,java/lang/String.indexOf156// For backward compatibility, allow space as separator also.157// exclude java/lang/String indexOf158// exclude,java/lang/String,indexOf159// For easy cut-and-paste of method names, allow VM output format160// as produced by Method::print_short_name:161// exclude java.lang.String::indexOf162// For simple implementation convenience here, convert them all to space.163164if (have_colon) {165if (*lp == '.') *lp = '/'; // dots build the package prefix166if (*lp == ':') *lp = ' ';167}168if (*lp == ',' || *lp == '.') *lp = ' ';169}170return true;171}172173bool MethodMatcher::match(Symbol* candidate, Symbol* match, Mode match_mode) const {174if (match_mode == Any) {175return true;176}177178if (match_mode == Exact) {179return candidate == match;180}181182ResourceMark rm;183const char * candidate_string = candidate->as_C_string();184const char * match_string = match->as_C_string();185186switch (match_mode) {187case Prefix:188return strstr(candidate_string, match_string) == candidate_string;189190case Suffix: {191size_t clen = strlen(candidate_string);192size_t mlen = strlen(match_string);193return clen >= mlen && strcmp(candidate_string + clen - mlen, match_string) == 0;194}195196case Substring:197return strstr(candidate_string, match_string) != NULL;198199default:200return false;201}202}203204static MethodMatcher::Mode check_mode(char name[], const char*& error_msg) {205int match = MethodMatcher::Exact;206if (name[0] == '*') {207if (strlen(name) == 1) {208return MethodMatcher::Any;209}210match |= MethodMatcher::Suffix;211memmove(name, name + 1, strlen(name + 1) + 1);212}213214size_t len = strlen(name);215if (len > 0 && name[len - 1] == '*') {216match |= MethodMatcher::Prefix;217name[--len] = '\0';218}219220if (strlen(name) == 0) {221error_msg = "** Not a valid pattern";222return MethodMatcher::Any;223}224225if (strstr(name, "*") != NULL) {226error_msg = " Embedded * not allowed";227return MethodMatcher::Unknown;228}229return (MethodMatcher::Mode)match;230}231232// Skip any leading spaces233void skip_leading_spaces(char*& line, int* total_bytes_read ) {234int bytes_read = 0;235sscanf(line, "%*[ \t]%n", &bytes_read);236if (bytes_read > 0) {237line += bytes_read;238*total_bytes_read += bytes_read;239}240}241242PRAGMA_DIAG_PUSH243// warning C4189: The file contains a character that cannot be represented244// in the current code page245PRAGMA_DISABLE_MSVC_WARNING(4819)246void MethodMatcher::parse_method_pattern(char*& line, const char*& error_msg, MethodMatcher* matcher) {247MethodMatcher::Mode c_match;248MethodMatcher::Mode m_match;249char class_name[256] = {0};250char method_name[256] = {0};251char sig[1024] = {0};252int bytes_read = 0;253int total_bytes_read = 0;254255assert(error_msg == NULL, "Dont call here with error_msg already set");256257if (!MethodMatcher::canonicalize(line, error_msg)) {258assert(error_msg != NULL, "Message must be set if parsing failed");259return;260}261262skip_leading_spaces(line, &total_bytes_read);263if (*line == '\0') {264error_msg = "Method pattern missing from command";265return;266}267268if (2 == sscanf(line, "%255" RANGESLASH "%*[ ]" "%255" RANGE0 "%n", class_name, method_name, &bytes_read)) {269c_match = check_mode(class_name, error_msg);270m_match = check_mode(method_name, error_msg);271272// Over-consumption273// method_name points to an option type or option name because the method name is not specified by users.274// In very rare case, the method name happens to be same as option type/name, so look ahead to make sure275// it doesn't show up again.276if ((OptionType::Unknown != CompilerOracle::parse_option_type(method_name) ||277CompileCommand::Unknown != CompilerOracle::parse_option_name(method_name)) &&278*(line + bytes_read) != '\0' &&279strstr(line + bytes_read, method_name) == NULL) {280error_msg = "Did not specify any method name";281method_name[0] = '\0';282return;283}284285if ((strchr(class_name, JVM_SIGNATURE_SPECIAL) != NULL) ||286(strchr(class_name, JVM_SIGNATURE_ENDSPECIAL) != NULL)) {287error_msg = "Chars '<' and '>' not allowed in class name";288return;289}290291if ((strchr(method_name, JVM_SIGNATURE_SPECIAL) != NULL) ||292(strchr(method_name, JVM_SIGNATURE_ENDSPECIAL) != NULL)) {293if (!vmSymbols::object_initializer_name()->equals(method_name) &&294!vmSymbols::class_initializer_name()->equals(method_name)) {295error_msg = "Chars '<' and '>' only allowed in <init> and <clinit>";296return;297}298}299300if (c_match == MethodMatcher::Unknown || m_match == MethodMatcher::Unknown) {301assert(error_msg != NULL, "Must have been set by check_mode()");302return;303}304305EXCEPTION_MARK;306Symbol* signature = NULL;307line += bytes_read;308bytes_read = 0;309310skip_leading_spaces(line, &total_bytes_read);311312// there might be a signature following the method.313// signatures always begin with ( so match that by hand314if (line[0] == '(') {315line++;316sig[0] = '(';317// scan the rest318if (1 == sscanf(line, "%1022[[);/" RANGEBASE "]%n", sig+1, &bytes_read)) {319if (strchr(sig, '*') != NULL) {320error_msg = " Wildcard * not allowed in signature";321return;322}323line += bytes_read;324}325signature = SymbolTable::new_symbol(sig);326}327Symbol* c_name = SymbolTable::new_symbol(class_name);328Symbol* m_name = SymbolTable::new_symbol(method_name);329330matcher->init(c_name, c_match, m_name, m_match, signature);331return;332} else {333error_msg = "Could not parse method pattern";334}335}336PRAGMA_DIAG_POP337338bool MethodMatcher::matches(const methodHandle& method) const {339Symbol* class_name = method->method_holder()->name();340Symbol* method_name = method->name();341Symbol* signature = method->signature();342343if (match(class_name, this->class_name(), _class_mode) &&344match(method_name, this->method_name(), _method_mode) &&345((this->signature() == NULL) || match(signature, this->signature(), Prefix))) {346return true;347}348return false;349}350351void MethodMatcher::print_symbol(outputStream* st, Symbol* h, Mode mode) {352if (mode == Suffix || mode == Substring || mode == Any) {353st->print("*");354}355if (mode != Any) {356h->print_utf8_on(st);357}358if (mode == Prefix || mode == Substring) {359st->print("*");360}361}362363void MethodMatcher::print_base(outputStream* st) {364ResourceMark rm;365366print_symbol(st, class_name(), _class_mode);367st->print(".");368print_symbol(st, method_name(), _method_mode);369if (signature() != NULL) {370signature()->print_utf8_on(st);371}372}373374BasicMatcher* BasicMatcher::parse_method_pattern(char* line, const char*& error_msg, bool expect_trailing_chars) {375assert(error_msg == NULL, "Don't call here with error_msg already set");376BasicMatcher* bm = new BasicMatcher();377MethodMatcher::parse_method_pattern(line, error_msg, bm);378if (error_msg != NULL) {379delete bm;380return NULL;381}382if (!expect_trailing_chars) {383// check for bad trailing characters384int bytes_read = 0;385sscanf(line, "%*[ \t]%n", &bytes_read);386if (line[bytes_read] != '\0') {387error_msg = "Unrecognized trailing text after method pattern";388delete bm;389return NULL;390}391}392return bm;393}394395bool BasicMatcher::match(const methodHandle& method) {396for (BasicMatcher* current = this; current != NULL; current = current->next()) {397if (current->matches(method)) {398return true;399}400}401return false;402}403404void InlineMatcher::print(outputStream* st) {405if (_inline_action == InlineMatcher::force_inline) {406st->print("+");407} else {408st->print("-");409}410print_base(st);411}412413InlineMatcher* InlineMatcher::parse_method_pattern(char* line, const char*& error_msg) {414assert(error_msg == NULL, "Dont call here with error_msg already set");415InlineMatcher* im = new InlineMatcher();416MethodMatcher::parse_method_pattern(line, error_msg, im);417if (error_msg != NULL) {418delete im;419return NULL;420}421return im;422}423424bool InlineMatcher::match(const methodHandle& method, int inline_action) {425for (InlineMatcher* current = this; current != NULL; current = current->next()) {426if (current->matches(method)) {427return (current->_inline_action == inline_action);428}429}430return false;431}432433InlineMatcher* InlineMatcher::parse_inline_pattern(char* str, const char*& error_msg) {434// check first token is +/-435InlineType _inline_action;436switch (str[0]) {437case '-':438_inline_action = InlineMatcher::dont_inline;439break;440case '+':441_inline_action = InlineMatcher::force_inline;442break;443default:444error_msg = "Missing leading inline type (+/-)";445return NULL;446}447str++;448449assert(error_msg == NULL, "error_msg must not be set yet");450InlineMatcher* im = InlineMatcher::parse_method_pattern(str, error_msg);451if (im == NULL) {452assert(error_msg != NULL, "Must have error message");453return NULL;454}455im->set_action(_inline_action);456return im;457}458459InlineMatcher* InlineMatcher::clone() {460InlineMatcher* m = new InlineMatcher();461m->_class_mode = _class_mode;462m->_method_mode = _method_mode;463m->_inline_action = _inline_action;464m->_class_name = _class_name;465if(_class_name != NULL) {466_class_name->increment_refcount();467}468m->_method_name = _method_name;469if (_method_name != NULL) {470_method_name->increment_refcount();471}472m->_signature = _signature;473if (_signature != NULL) {474_signature->increment_refcount();475}476return m;477}478479480