2019 के लिए जावा परियोजनाओं में शीर्ष 10 कीड़े


2019 समाप्त हो रहा है, और पीवीएस-स्टूडियो टीम आउटगोइंग वर्ष के परिणामों का योग कर रही है। 2019 की शुरुआत में, हमने जावा भाषा का समर्थन करके विश्लेषक की क्षमताओं का विस्तार किया। इसलिए, खुली परियोजनाओं की जाँच के बारे में हमारे प्रकाशनों की सूची जावा परियोजनाओं की समीक्षा के साथ फिर से भर दी गई। वर्ष के दौरान बहुत सारी गलतियाँ पाई गईं, और हमने उनमें से शीर्ष 10 सबसे दिलचस्प तैयार करने का फैसला किया।


दसवां स्थान: प्रतिष्ठित बाइट


स्रोत: स्थैतिक विश्लेषक पीवीएस-स्टूडियो द्वारा आरपीसी फ्रेमवर्क अपाचे डब्बू के स्रोत कोड का विश्लेषण

V6007 अभिव्यक्ति 'endKey [i] <0xff' हमेशा सच होती है। OptionUtil.java (32)

public static final ByteSequence prefixEndOf(ByteSequence prefix) { byte[] endKey = prefix.getBytes().clone(); for (int i = endKey.length - 1; i >= 0; i--) { if (endKey[i] < 0xff) { // <= endKey[i] = (byte) (endKey[i] + 1); return ByteSequence.from(Arrays.copyOf(endKey, i + 1)); } } return ByteSequence.from(NO_PREFIX_END); } 

कई प्रोग्रामर मानते हैं कि बाइट नाम का एक प्रकार अहस्ताक्षरित होगा। और वास्तव में, विभिन्न भाषाओं में अक्सर ऐसा ही होता है। उदाहरण के लिए, C # में, बाइट प्रकार अहस्ताक्षरित है। जावा में, यह मामला नहीं है।

स्थिति एंडके [i] <0xff में, विधि के लेखक हेक्साडेसिमल प्रतिनिधित्व में प्रस्तुत संख्या 255 (0xff) के साथ प्रकार बाइट के एक चर की तुलना करते हैं। जाहिरा तौर पर, विधि लिखते समय, डेवलपर यह भूल गया कि जावा में टाइप बाइट के मूल्यों की सीमा [-128, 127] है। यह स्थिति हमेशा सत्य होती है, इसलिए लूप के लिए हमेशा एंडके सरणी के अंतिम तत्व की प्रक्रिया होगी।

नौवां स्थान: एक में दो


स्रोत: जावा के लिए पीवीएस-स्टूडियो को पथ पर भेजा जाता है। अगला पड़ाव है एलीटेसर्च

V6007 अभिव्यक्ति '(int) x <0' हमेशा गलत है। BCrypt.java (429)

V6025 संभवतः सूचकांक '(int) x' सीमा से बाहर है। BCrypt.java (431)

 private static byte char64(char x) { if ((int)x < 0 || (int)x > index_64.length) return -1; return index_64[(int)x]; } 

आज हमारे पास एक विशेष प्रस्ताव है! एक बार में एक विधि में दो त्रुटियां। पहली त्रुटि का कारण चार प्रकार है, जो जावा में अहस्ताक्षरित है, यही कारण है कि स्थिति (int) x <0 हमेशा गलत है। दूसरी त्रुटि इंडेक्स_64 सरणी की सीमा से बाहर जाने वाली केनाल है जब (int) x == index_64.length । स्थिति (int) x> index_64.length के कारण यह स्थिति संभव है। सरणी की सीमा से बाहर जाने से छुटकारा पाने के लिए, '>' को '> =' के साथ बदलना आवश्यक है। सही स्थिति यह होगी: (int) x> = index_64.length

आठवां स्थान: निर्णय और उसके परिणाम


स्रोत: PVS-Studio के साथ CUBA प्लेटफ़ॉर्म कोड विश्लेषण

V6007 अभिव्यक्ति 'पिछले MenuItemFlatIndex > = 0' हमेशा सच होता है। क्यूबासाइडमेन्यूविजेट.जावा (328)

 protected MenuItemWidget findNextMenuItem(MenuItemWidget currentItem) { List<MenuTreeNode> menuTree = buildVisibleTree(this); List<MenuItemWidget> menuItemWidgets = menuTreeToList(menuTree); int menuItemFlatIndex = menuItemWidgets.indexOf(currentItem); int previousMenuItemFlatIndex = menuItemFlatIndex + 1; if (previousMenuItemFlatIndex >= 0) { // <= return menuItemWidgets.get(previousMenuItemFlatIndex); } return null; } 

FindNextMenuItem विधि के लेखक मेनू -emWidgets सूची currentItem शामिल नहीं है, तो indexOf विधि द्वारा -1 से छुटकारा पाने के लिए करना चाहता है। ऐसा करने के लिए, वह एक indexOf परिणाम ( menuItemFlatIndex चर) को जोड़ता है और परिणामी मान को पिछले MenuItemFlatIndex चर में संग्रहीत करता है, जो आगे विधि में उपयोग किया जाता है। -1 समस्या का यह समाधान असफल है क्योंकि यह एक ही बार में कई त्रुटियों को जन्म देता है:

  • वापसी शून्य कोड कभी भी निष्पादित नहीं किया जाएगा, क्योंकि अभिव्यक्ति पिछले MenuItemFlatIndex > = 0 हमेशा सत्य होता है, जिसका अर्थ है कि findNextMenuItem विधि से वापसी हमेशा if के अंदर होगी ;
  • एक IndexOutOfBoundsException मेनूइटemWidgets सूची खाली होने पर फेंक दिया जाएगा, क्योंकि खाली सूची का पहला तत्व एक्सेस हो जाएगा;
  • एक indexOutOfBoundsException अपवाद तब होगा जब currentItem तर्क menuItemWidget सूची में अंतिम होगा।

सातवीं जगह: कुछ भी नहीं से एक फ़ाइल बनाना


स्रोत: हुआवेई क्लाउड: आज पीवीएस-स्टूडियो में बादल छाए हुए हैं

V6008 'डेटाटेम्पाइल' के संभावित अशक्तता। कैचेमनगर.जावा (91)

 @Override public void putToCache(PutRecordsRequest putRecordsRequest) { .... if (dataTmpFile == null || !dataTmpFile.exists()) { try { dataTmpFile.createNewFile(); // <= } catch (IOException e) { LOGGER.error("Failed to create cache tmp file, return.", e); return; } } .... } 

PutToCache विधि लिखते समय , प्रोग्रामर ने डेटा में एक टाइपो बनाया TTFile == null || एक नया dataTmpFile.createNewFile () फ़ाइल बनाने से पहले dataTmpFile.exists ()। एक टाइपो ऑपरेटर '==' के बजाय '=!' का उपयोग होता है। यह टाइपो createNewFile विधि को कॉल करते समय NullPointerException को फेंक देगा। एक टाइपो को सही करने के बाद की स्थिति इस प्रकार है:

 if (dataTmpFile != null || !dataTmpFile.exists()) 

“त्रुटि पाई गई, सही किया गया। आप आराम कर सकते हैं, “आप सोचेंगे। लेकिन कोई फर्क नहीं पड़ता कि कैसे!

एक त्रुटि को सुधारने के बाद, हमने दूसरा पाया। DataTmpFile.exists () पर कॉल करते समय, एक NullPointerException हो सकती है। अब, अपवाद से छुटकारा पाने के लिए, 'को प्रतिस्थापित करना आवश्यक है।' '&&' पर। वह स्थिति जिसके तहत सभी त्रुटियां गायब हैं, निम्नानुसार होगी:

 if (dataTmpFile != null && !dataTmpFile.exists()) 

छठा स्थान: एक बहुत ही अजीब तार्किक त्रुटि


स्रोत: जावा के लिए पीवीएस-स्टूडियो

V6007 [CWE-570] अभिव्यक्ति '"0"। असमान (पाठ)' हमेशा गलत है। ConvertIntegerToDecimalPredicate.java 46

 public boolean satisfiedBy(@NotNull PsiElement element) { .... @NonNls final String text = expression.getText().replaceAll("_", ""); if (text == null || text.length() < 2) { return false; } if ("0".equals(text) || "0L".equals(text) || "0l".equals(text)) {// <= return false; } return text.charAt(0) == '0'; } 

यह विधि दिलचस्प है कि इसमें एक स्पष्ट तार्किक त्रुटि है। यदि संतुष्ट विधि पहले मान के बाद मान वापस नहीं करती है , तो यह ज्ञात हो जाता है कि पाठ स्ट्रिंग में कम से कम दो वर्ण हैं। इस वजह से, यदि निरर्थक है तो पहले "0" .equals (पाठ) की जाँच करें। क्या वास्तव में डेवलपर मतलब एक रहस्य बना हुआ है।

पांचवा स्थान: यह एक मोड़ है!


स्रोत: पीवीएस-स्टूडियो अपाचे हाइव पर जाकर

V6034 'बिटश शिफ्टइन्वायर - 1' के मान से शिफ्ट प्रकार के आकार के साथ असंगत हो सकता है: 'बिटश शिफ्ट्सवर्ल्ड - 1' = [-1 ... 30]। अनसाइन्टइंटेंट २००s.जावा (१igned ९ १)

 private void shiftRightDestructive(int wordShifts, int bitShiftsInWord, boolean roundUp) { if (wordShifts == 0 && bitShiftsInWord == 0) { return; } assert (wordShifts >= 0); assert (bitShiftsInWord >= 0); assert (bitShiftsInWord < 32); if (wordShifts >= 4) { zeroClear(); return; } final int shiftRestore = 32 - bitShiftsInWord; // check this because "123 << 32" will be 123. final boolean noRestore = bitShiftsInWord == 0; final int roundCarryNoRestoreMask = 1 << 31; final int roundCarryMask = (1 << (bitShiftsInWord - 1)); // <= .... } 

इनपुट तर्कों के साथ शब्दशः = 3 और बिटशिफ्टइवॉर्ड = 0 , चर गोलकार्यमस्क , जो बिट शिफ्ट के परिणाम को संग्रहीत करता है (1 << (bitShiftsInWord - 1)) , एक नकारात्मक संख्या होगी। शायद डेवलपर को इस व्यवहार की उम्मीद नहीं थी।

चौथा स्थान: क्या टहलने के लिए अपवाद सामने आएंगे?


स्रोत: पीवीएस-स्टूडियो अपाचे हाइव पर जाकर

V6051 'अंत' ब्लॉक में 'रिटर्न' स्टेटमेंट के उपयोग से अनचाहे अपवादों का नुकसान हो सकता है। ObjectStore.java (9080)

 private List<MPartitionColumnStatistics> getMPartitionColumnStatistics(....) throws NoSuchObjectException, MetaException { boolean committed = false; try { .... /*some actions*/ committed = commitTransaction(); return result; } catch (Exception ex) { LOG.error("Error retrieving statistics via jdo", ex); if (ex instanceof MetaException) { throw (MetaException) ex; } throw new MetaException(ex.getMessage()); } finally { if (!committed) { rollbackTransaction(); return Lists.newArrayList(); } } } 

GetMPartitionColumnStatistics पद्धति की घोषणा हमारे लिए झूठ है, यह कहते हुए कि यह एक अपवाद फेंक सकता है। जब कोई अपवाद प्रयास में होता है, तो प्रतिबद्ध चर गलत रहता है , इसलिए, अंत में ब्लॉक में रिटर्न स्टेटमेंट विधि से मान लौटाता है, और सभी फेंके गए अपवाद खो जाते हैं और विधि के बाहर संसाधित नहीं किया जा सकता है। इस प्रकार, इस पद्धति में उठाया गया कोई भी अपवाद कभी भी इससे बाहर नहीं निकल पाएगा।

तीसरा स्थान: मैं घुमाता हूं, घुमाता हूं, मैं एक नया मुखौटा प्राप्त करना चाहता हूं


स्रोत: पीवीएस-स्टूडियो अपाचे हाइव पर जाकर

V6034 शिफ्ट 'जे' के मान से आकार के अनुसार असंगत हो सकता है: 'जे' = [0 ... 63]। IoTrace.java (272)

 public void logSargResult(int stripeIx, boolean[] rgsToRead) { .... for (int i = 0, valOffset = 0; i < elements; ++i, valOffset += 64) { long val = 0; for (int j = 0; j < 64; ++j) { int ix = valOffset + j; if (rgsToRead.length == ix) break; if (!rgsToRead[ix]) continue; val = val | (1 << j); // <= } .... } .... } 

बिटवाइज़ शिफ्ट से जुड़ी एक और गलती, लेकिन इस बार न केवल वह मामले में शामिल था। लूप के लिए आंतरिक में, चर j [0 ... 63] का उपयोग लूप काउंटर के रूप में किया जाता है। यह काउंटर 1 << j की एक बिट शिफ्ट में शामिल है। कुछ भी नहीं है, मुसीबत को दर्शाती है, हालांकि, पूर्णांक प्रकार (32-बिट मान) का पूर्णांक शाब्दिक '' यहाँ आता है। यह निम्नानुसार है कि बिट शिफ्ट के परिणाम 31 से अधिक होने के बाद दोहराना शुरू कर देंगे। यदि वर्णित व्यवहार अवांछनीय है, तो यूनिट को लंबे समय तक दर्शाया जाना चाहिए, उदाहरण के लिए, 1L << j या (long) 1 << j

दूसरा स्थान: प्रारंभिक आदेश


स्रोत: हुआवेई क्लाउड: आज पीवीएस-स्टूडियो में बादल छाए हुए हैं

V6050 क्लास आरंभीकरण चक्र मौजूद है। 'लॉग' के आरंभीकरण से पहले 'INSTANCE' का प्रारंभ दिखाई देता है। अनट्रस्टेडएसएसएल.जावा (32), अनट्रस्टिएसएसएल.जवा (59), अनट्रिस्वर्डएसएसएल.जावा (33)

 public class UntrustedSSL { private static final UntrustedSSL INSTANCE = new UntrustedSSL(); private static final Logger LOG = LoggerFactory.getLogger(UntrustedSSL.class); .... private UntrustedSSL() { try { .... } catch (Throwable t) { LOG.error(t.getMessage(), t); // <= } } } 

फ़ील्ड में घोषित किए जाने वाले फ़ील्ड का क्रम महत्वपूर्ण है क्योंकि फ़ील्ड उसी क्रम में आरंभिक होते हैं, जिस क्रम में उन्हें घोषित किया जाता है। हालाँकि, जब वे इसके बारे में भूल जाते हैं, तो सूक्ष्म गलतियाँ होती हैं, जैसे यह।

विश्लेषक ने संकेत दिया कि स्थैतिक लॉग फ़ील्ड को कंस्ट्रक्टर में तब डाला जाता है जब इसे नल के लिए आरम्भ किया जाता है, जो NullPointerException -> ExceptionInInitializerError अपवाद श्रृंखला की ओर जाता है।

"क्यों, कंस्ट्रक्टर कॉल के समय, स्थैतिक लॉग फ़ील्ड शून्य है ?" आप पूछते हैं।

ExceptionInInitializerError अपवाद एक संकेत है। तथ्य यह है कि इस कंस्ट्रक्टर का उपयोग लॉग फ़ील्ड की तुलना में कक्षा में घोषित INSTANCE स्टैटिक फील्ड को इनिशियलाइज़ करने के लिए किया जाता है। इसलिए, निर्माता कॉल के समय, लॉग फ़ील्ड को अभी भी प्रारंभ नहीं किया गया है। कोड को सही ढंग से काम करने के लिए, निर्माता को कॉल करने से पहले लॉग फ़ील्ड को इनिशियलाइज़ करना आवश्यक है।

पहला स्थान: कॉपी-पेस्ट-उन्मुख प्रोग्रामिंग


स्रोत: अपाचे Hadoop कोड गुणवत्ता: उत्पादन वी.एस. परीक्षण

V6072 दो समान कोड टुकड़े पाए गए। शायद, यह एक टाइपो है और 'लोकलफेयर' के बजाय 'लोकलफाइल्स' चर का उपयोग किया जाना चाहिए। LocalDistributedCacheManager.java (183), LocalDistributedCacheManager.java (178), LocalDistributedCacheManager.java (176), LocalDistributedCacheManager.java (181)

 public synchronized void setup(JobConf conf, JobID jobId) throws IOException { .... // Update the configuration object with localized data. if (!localArchives.isEmpty()) { conf.set(MRJobConfig.CACHE_LOCALARCHIVES, StringUtils .arrayToString(localArchives.toArray(new String[localArchives // <= .size()]))); } if (!localFiles.isEmpty()) { conf.set(MRJobConfig.CACHE_LOCALFILES, StringUtils .arrayToString(localFiles.toArray(new String[localArchives // <= .size()]))); } .... } 

और पहला स्थान कॉपी-पेस्ट के द्वारा लिया जाता है, या यूँ कहें कि एक गलती जो इस पापी बात को करने वाले की लापरवाही के कारण उत्पन्न हुई। यह अत्यधिक संभावना है कि दूसरा यदि चर के प्रतिस्थापन के साथ पहले की कॉपी-पेस्ट द्वारा बनाया गया था:

  • लोकलफेयर पर स्थानीय लोगों ;
  • MRJobConfig.CACHE_LOCALARCHIVES पर MRJobConfig.CACHE_LOCALFILES

हालांकि, इस तरह के एक सरल ऑपरेशन के साथ, एक त्रुटि भी की गई थी, क्योंकि स्थानीय एरिकाइव्स चर अभी भी दूसरे में इस्तेमाल किया गया था अगर दूसरे विश्लेषक में लाइन, हालांकि लोकलफाइल्स का उपयोग सबसे अधिक होने की संभावना थी

निष्कर्ष


विकास के बाद के चरणों में या किसी परियोजना के जारी होने के बाद मिली त्रुटियों के सुधार के लिए महत्वपूर्ण संसाधनों की आवश्यकता होती है। पीवीएस-स्टूडियो स्थैतिक विश्लेषक कोड लिखते समय त्रुटियों का पता लगाने को सरल करता है, जो उन्हें ठीक करने में खर्च किए गए संसाधनों की मात्रा को काफी कम कर देता है। विश्लेषक के निरंतर उपयोग ने पहले ही कई कंपनियों के डेवलपर्स के जीवन को सरल बना दिया है। यदि आप बहुत खुशी के साथ कार्यक्रम करना चाहते हैं, तो हमारे विश्लेषक का प्रयास करें।

हमारी टीम वहां नहीं रुकेगी और विश्लेषक सुधार और सुधार जारी रखेगी। अगले वर्ष और भी दिलचस्प बग के साथ नए निदान और लेखों की अपेक्षा करें।

मैं तुम्हें प्यार साहसिक देखो! सबसे पहले, 2019 के लिए C # परियोजनाओं में शीर्ष 10 त्रुटियां जीतीं, और अब जावा पर काबू पाने में सक्षम था! सी ++ परियोजनाओं में 2019 की सबसे अच्छी गलतियों के बारे में लेख में अगले स्तर पर आपका स्वागत है।





यदि आप इस लेख को अंग्रेजी बोलने वाले दर्शकों के साथ साझा करना चाहते हैं, तो कृपया अनुवाद के लिंक का उपयोग करें: वालेरी कोमारोव। 2019 में जावा प्रोजेक्ट्स में शीर्ष 10 कीड़े मिले

Source: https://habr.com/ru/post/hi481186/


All Articles