001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.coding;
021
022import java.util.Deque;
023import java.util.Map;
024import java.util.Queue;
025import java.util.Set;
026
027import com.google.common.collect.ImmutableSet;
028import com.google.common.collect.Lists;
029import com.google.common.collect.Maps;
030import com.google.common.collect.Sets;
031import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
032import com.puppycrawl.tools.checkstyle.api.DetailAST;
033import com.puppycrawl.tools.checkstyle.api.TokenTypes;
034import com.puppycrawl.tools.checkstyle.utils.ScopeUtils;
035
036/**
037 * <p>Checks that code doesn't rely on the &quot;this&quot; default.
038 * That is references to instance variables and methods of the present
039 * object are explicitly of the form &quot;this.varName&quot; or
040 * &quot;this.methodName(args)&quot;.
041 * </p>
042 *
043 * <p>Warning: the Check is very controversial and not that actual nowadays.</p>
044 *
045 * <p>Examples of use:
046 * <pre>
047 * &lt;module name=&quot;RequireThis&quot;/&gt;
048 * </pre>
049 * An example of how to configure to check {@code this} qualifier for
050 * methods only:
051 * <pre>
052 * &lt;module name=&quot;RequireThis&quot;&gt;
053 *   &lt;property name=&quot;checkFields&quot; value=&quot;false&quot;/&gt;
054 *   &lt;property name=&quot;checkMethods&quot; value=&quot;true&quot;/&gt;
055 * &lt;/module&gt;
056 * </pre>
057 *
058 * <p>Rationale:</p>
059 * <ol>
060 *   <li>
061 *     The same notation/habit for C++ and Java (C++ have global methods, so having
062 *     &quot;this.&quot; do make sense in it to distinguish call of method of class
063 *     instead of global).
064 *   </li>
065 *   <li>
066 *     Non-IDE development (ease of refactoring, some clearness to distinguish
067 *     static and non-static methods).
068 *   </li>
069 * </ol>
070 *
071 * <p>Limitations: Nothing is currently done about static variables
072 * or catch-blocks.  Static methods invoked on a class name seem to be OK;
073 * both the class name and the method name have a DOT parent.
074 * Non-static methods invoked on either this or a variable name seem to be
075 * OK, likewise.</p>
076 *
077 * @author Stephen Bloch
078 * @author o_sukhodolsky
079 */
080public class RequireThisCheck extends AbstractCheck {
081
082    /**
083     * A key is pointing to the warning message text in "messages.properties"
084     * file.
085     */
086    public static final String MSG_METHOD = "require.this.method";
087
088    /**
089     * A key is pointing to the warning message text in "messages.properties"
090     * file.
091     */
092    public static final String MSG_VARIABLE = "require.this.variable";
093    /**
094     * Set of all declaration tokens.
095     */
096    private static final ImmutableSet<Integer> DECLARATION_TOKENS = ImmutableSet.of(
097            TokenTypes.VARIABLE_DEF,
098            TokenTypes.CTOR_DEF,
099            TokenTypes.METHOD_DEF,
100            TokenTypes.CLASS_DEF,
101            TokenTypes.ENUM_DEF,
102            TokenTypes.INTERFACE_DEF,
103            TokenTypes.PARAMETER_DEF,
104            TokenTypes.TYPE_ARGUMENT
105    );
106
107    /**
108     * Tree of all the parsed frames.
109     */
110    private Map<DetailAST, AbstractFrame> frames;
111
112    /**
113     * Frame for the currently processed AST.
114     */
115    private AbstractFrame current;
116
117    /** Whether we should check fields usage. */
118    private boolean checkFields = true;
119    /** Whether we should check methods usage. */
120    private boolean checkMethods = true;
121
122    /**
123     * Setter for checkFields property.
124     * @param checkFields should we check fields usage or not.
125     */
126    public void setCheckFields(boolean checkFields) {
127        this.checkFields = checkFields;
128    }
129
130    /**
131     * Setter for checkMethods property.
132     * @param checkMethods should we check methods usage or not.
133     */
134    public void setCheckMethods(boolean checkMethods) {
135        this.checkMethods = checkMethods;
136    }
137
138    @Override
139    public int[] getDefaultTokens() {
140        return getAcceptableTokens();
141    }
142
143    @Override
144    public int[] getRequiredTokens() {
145        return getAcceptableTokens();
146    }
147
148    @Override
149    public int[] getAcceptableTokens() {
150        return new int[] {
151            TokenTypes.CLASS_DEF,
152            TokenTypes.INTERFACE_DEF,
153            TokenTypes.ENUM_DEF,
154            TokenTypes.CTOR_DEF,
155            TokenTypes.METHOD_DEF,
156            TokenTypes.SLIST,
157            TokenTypes.IDENT,
158        };
159    }
160
161    @Override
162    public void beginTree(DetailAST rootAST) {
163        final Deque<AbstractFrame> frameStack = Lists.newLinkedList();
164
165        frames = Maps.newHashMap();
166
167        DetailAST curNode = rootAST;
168        while (curNode != null) {
169            collectDeclarations(frameStack, curNode);
170            DetailAST toVisit = curNode.getFirstChild();
171            while (curNode != null && toVisit == null) {
172                endCollectingDeclarations(frameStack, curNode);
173                toVisit = curNode.getNextSibling();
174                if (toVisit == null) {
175                    curNode = curNode.getParent();
176                }
177            }
178            curNode = toVisit;
179        }
180    }
181
182    @Override
183    public void visitToken(DetailAST ast) {
184        switch (ast.getType()) {
185            case TokenTypes.IDENT :
186                processIdent(ast);
187                break;
188            case TokenTypes.CLASS_DEF :
189            case TokenTypes.INTERFACE_DEF :
190            case TokenTypes.ENUM_DEF :
191            case TokenTypes.ANNOTATION_DEF :
192            case TokenTypes.SLIST :
193            case TokenTypes.METHOD_DEF :
194            case TokenTypes.CTOR_DEF :
195                current = frames.get(ast);
196                break;
197            default :
198                // do nothing
199        }
200    }
201
202    /**
203     * Checks if a given IDENT is method call or field name which
204     * require explicit {@code this} qualifier.
205     *
206     * @param ast IDENT to check.
207     */
208    private void processIdent(DetailAST ast) {
209        final int parentType = ast.getParent().getType();
210        switch (parentType) {
211            case TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR:
212            case TokenTypes.ANNOTATION:
213            case TokenTypes.ANNOTATION_FIELD_DEF:
214                // no need to check annotations content
215                break;
216            case TokenTypes.METHOD_CALL:
217                // let's check method calls
218                if (checkMethods) {
219                    final AbstractFrame frame = checkMethod(ast);
220                    if (frame != null) {
221                        logViolation(MSG_METHOD, ast, frame);
222                    }
223                }
224                break;
225            default:
226                if (checkFields) {
227                    final AbstractFrame frame = processField(ast, parentType);
228                    if (frame != null) {
229                        logViolation(MSG_VARIABLE, ast, frame);
230                    }
231                }
232                break;
233        }
234    }
235
236    /**
237     * Helper method to log a LocalizedMessage.
238     * @param ast a node to get line id column numbers associated with the message.
239     * @param msgKey key to locale message format.
240     * @param frame the frame, where the violation is found.
241     */
242    private void logViolation(String msgKey, DetailAST ast, AbstractFrame frame) {
243        if (frame.getFrameName().equals(getNearestClassFrameName())) {
244            log(ast, msgKey, ast.getText(), "");
245        }
246        else {
247            log(ast, msgKey, ast.getText(), frame.getFrameName() + '.');
248        }
249    }
250
251    /**
252     * Process validation of Field.
253     * @param ast field definition ast token
254     * @param parentType type of the parent
255     * @return frame, where the field is declared, if the violation is found and null otherwise
256     */
257    private AbstractFrame processField(DetailAST ast, int parentType) {
258        final boolean importOrPackage = ScopeUtils.getSurroundingScope(ast) == null;
259        final boolean methodNameInMethodCall = parentType == TokenTypes.DOT
260                && ast.getPreviousSibling() != null;
261        final boolean typeName = parentType == TokenTypes.TYPE
262                || parentType == TokenTypes.LITERAL_NEW;
263        AbstractFrame frame = null;
264
265        if (!importOrPackage
266                && !methodNameInMethodCall
267                && !typeName
268                && !isDeclarationToken(parentType)) {
269            frame = checkField(ast);
270        }
271        return frame;
272    }
273
274    /**
275     * Parse the next AST for declarations.
276     *
277     * @param frameStack Stack containing the FrameTree being built
278     * @param ast AST to parse
279     */
280    private static void collectDeclarations(Deque<AbstractFrame> frameStack,
281        DetailAST ast) {
282        final AbstractFrame frame = frameStack.peek();
283        switch (ast.getType()) {
284            case TokenTypes.VARIABLE_DEF :
285                collectVariableDeclarations(ast, frame);
286                break;
287            case TokenTypes.PARAMETER_DEF :
288                final DetailAST parameterIdent = ast.findFirstToken(TokenTypes.IDENT);
289                frame.addIdent(parameterIdent);
290                break;
291            case TokenTypes.CLASS_DEF :
292            case TokenTypes.INTERFACE_DEF :
293            case TokenTypes.ENUM_DEF :
294            case TokenTypes.ANNOTATION_DEF :
295                final DetailAST classIdent = ast.findFirstToken(TokenTypes.IDENT);
296                frameStack.addFirst(new ClassFrame(frame, classIdent.getText()));
297                break;
298            case TokenTypes.SLIST :
299                frameStack.addFirst(new BlockFrame(frame));
300                break;
301            case TokenTypes.METHOD_DEF :
302                final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
303                if (frame.getType() == FrameType.CLASS_FRAME) {
304                    final DetailAST mods =
305                            ast.findFirstToken(TokenTypes.MODIFIERS);
306                    if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
307                        ((ClassFrame) frame).addStaticMethod(ident);
308                    }
309                    else {
310                        ((ClassFrame) frame).addInstanceMethod(ident);
311                    }
312                }
313                frameStack.addFirst(new MethodFrame(frame));
314                break;
315            case TokenTypes.CTOR_DEF :
316                frameStack.addFirst(new MethodFrame(frame));
317                break;
318            default:
319                // do nothing
320        }
321    }
322
323    /**
324     * Collect Variable Declarations.
325     * @param ast variable token
326     * @param frame current frame
327     */
328    private static void collectVariableDeclarations(DetailAST ast, AbstractFrame frame) {
329        final DetailAST ident = ast.findFirstToken(TokenTypes.IDENT);
330        if (frame.getType() == FrameType.CLASS_FRAME) {
331            final DetailAST mods =
332                    ast.findFirstToken(TokenTypes.MODIFIERS);
333            if (ScopeUtils.isInInterfaceBlock(ast)
334                    || mods.branchContains(TokenTypes.LITERAL_STATIC)) {
335                ((ClassFrame) frame).addStaticMember(ident);
336            }
337            else {
338                ((ClassFrame) frame).addInstanceMember(ident);
339            }
340        }
341        else {
342            frame.addIdent(ident);
343        }
344    }
345
346    /**
347     * End parsing of the AST for declarations.
348     *
349     * @param frameStack Stack containing the FrameTree being built
350     * @param ast AST that was parsed
351     */
352    private void endCollectingDeclarations(Queue<AbstractFrame> frameStack,
353        DetailAST ast) {
354        switch (ast.getType()) {
355            case TokenTypes.CLASS_DEF :
356            case TokenTypes.INTERFACE_DEF :
357            case TokenTypes.ENUM_DEF :
358            case TokenTypes.ANNOTATION_DEF :
359            case TokenTypes.SLIST :
360            case TokenTypes.METHOD_DEF :
361            case TokenTypes.CTOR_DEF :
362                frames.put(ast, frameStack.poll());
363                break;
364            default :
365                // do nothing
366        }
367    }
368
369    /**
370     * Check if given name is a name for class field in current environment.
371     * @param ast an IDENT ast to check
372     * @return frame, where the field is declared, if the violation is found and null otherwise
373     */
374    private AbstractFrame checkField(DetailAST ast) {
375        final AbstractFrame frame = findFrame(ast, false);
376        if (frame != null
377                && frame.getType() == FrameType.CLASS_FRAME
378                && ((ClassFrame) frame).hasInstanceMember(ast)) {
379            return frame;
380        }
381        return null;
382    }
383
384    /**
385     * Check if given name is a name for class method in current environment.
386     * @param ast the IDENT ast of the name to check
387     * @return frame, where the method is declared, if the violation is found and null otherwise
388     */
389    private AbstractFrame checkMethod(DetailAST ast) {
390        final AbstractFrame frame = findFrame(ast, true);
391        if (frame != null
392            && ((ClassFrame) frame).hasInstanceMethod(ast)
393            && !((ClassFrame) frame).hasStaticMethod(ast)) {
394            return frame;
395        }
396        return null;
397    }
398
399    /**
400     * Find frame containing declaration.
401     * @param name IDENT ast of the declaration to find.
402     * @param lookForMethod whether we are looking for a method name.
403     * @return AbstractFrame containing declaration or null.
404     */
405    private AbstractFrame findFrame(DetailAST name, boolean lookForMethod) {
406        if (current == null) {
407            return null;
408        }
409        else {
410            return current.getIfContains(name, lookForMethod);
411        }
412    }
413
414    /**
415     * Check that token is related to Definition tokens.
416     * @param parentType token Type
417     * @return true if token is related to Definition Tokens
418     */
419    private static boolean isDeclarationToken(int parentType) {
420        return DECLARATION_TOKENS.contains(parentType);
421    }
422
423    /**
424     * Get the name of the nearest parent ClassFrame.
425     * @return the name of the nearest parent ClassFrame.
426     */
427    private String getNearestClassFrameName() {
428        AbstractFrame frame = current;
429        while (frame.getType() != FrameType.CLASS_FRAME) {
430            frame = frame.getParent();
431        }
432        return frame.getFrameName();
433    }
434
435    /** An AbstractFrame type. */
436    private enum FrameType {
437        /** Class frame type. */
438        CLASS_FRAME,
439        /** Method frame type. */
440        METHOD_FRAME,
441        /** Block frame type. */
442        BLOCK_FRAME,
443    }
444
445    /**
446     * A declaration frame.
447     * @author Stephen Bloch
448     */
449    private abstract static class AbstractFrame {
450        /** Set of name of variables declared in this frame. */
451        private final Set<DetailAST> varIdents;
452
453        /**
454         * Parent frame.
455         */
456        private final AbstractFrame parent;
457
458        /**
459         * Frame name.
460         */
461        private final String frameName;
462
463        /**
464         * Constructor -- invokable only via super() from subclasses.
465         *
466         * @param parent parent frame
467         * @param frameName frame name
468         */
469        protected AbstractFrame(AbstractFrame parent, String frameName) {
470            this.parent = parent;
471            this.frameName = frameName;
472            varIdents = Sets.newHashSet();
473        }
474
475        /**
476         * Get the type of the frame.
477         * @return a FrameType.
478         */
479        protected abstract FrameType getType();
480
481        /**
482         * Add a name to the frame.
483         * @param identToAdd the name we're adding
484         */
485        private void addIdent(DetailAST identToAdd) {
486            varIdents.add(identToAdd);
487        }
488
489        protected AbstractFrame getParent() {
490            return parent;
491        }
492
493        protected String getFrameName() {
494            return frameName;
495        }
496
497        /** Check whether the frame contains a field or a variable with the given name.
498         * @param nameToFind the IDENT ast of the name we're looking for
499         * @return whether it was found
500         */
501        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
502            return containsFieldOrVariableDef(varIdents, nameToFind);
503        }
504
505        /** Check whether the frame contains a given name.
506         * @param nameToFind IDENT ast of the name we're looking for.
507         * @param lookForMethod whether we are looking for a method name.
508         * @return whether it was found.
509         */
510        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
511            final AbstractFrame frame;
512
513            if (!lookForMethod
514                && containsFieldOrVariable(nameToFind)) {
515                frame = this;
516            }
517            else {
518                frame = parent.getIfContains(nameToFind, lookForMethod);
519            }
520            return frame;
521        }
522
523        /**
524         * Whether the set contains a declaration with the text of the specified
525         * IDENT ast and it is declared in a proper position.
526         * @param set the set of declarations.
527         * @param ident the specified IDENT ast
528         * @return true if the set contains a declaration with the text of the specified
529         *         IDENT ast and it is declared in a proper position.
530         */
531        protected boolean containsFieldOrVariableDef(Set<DetailAST> set, DetailAST ident) {
532            boolean result = false;
533            for (DetailAST ast: set) {
534                if (isProperDefinition(ident, ast)) {
535                    result = true;
536                    break;
537                }
538            }
539            return result;
540        }
541
542        /**
543         * Whether the definition is correspondent to the IDENT.
544         * @param ident the IDENT ast to check.
545         * @param ast the IDENT ast of the definition to check.
546         * @return true if ast is correspondent to ident.
547         */
548        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
549            final String nameToFind = ident.getText();
550            return nameToFind.equals(ast.getText())
551                && checkPosition(ast, ident);
552        }
553
554        /**
555         * Whether the declaration is located before the checked ast.
556         * @param ast1 the IDENT ast of the declaration.
557         * @param ast2 the IDENT ast to check.
558         * @return true, if the declaration is located before the checked ast.
559         */
560        private static boolean checkPosition(DetailAST ast1, DetailAST ast2) {
561            boolean result = false;
562            if (ast1.getLineNo() < ast2.getLineNo()
563                || ast1.getLineNo() == ast2.getLineNo()
564                && ast1.getColumnNo() < ast2.getColumnNo()) {
565                result = true;
566            }
567            return result;
568        }
569    }
570
571    /**
572     * A frame initiated at method definition; holds parameter names.
573     * @author Stephen Bloch
574     */
575    private static class MethodFrame extends AbstractFrame {
576        /**
577         * Creates method frame.
578         * @param parent parent frame
579         */
580        protected MethodFrame(AbstractFrame parent) {
581            super(parent, null);
582        }
583
584        @Override
585        protected FrameType getType() {
586            return FrameType.METHOD_FRAME;
587        }
588    }
589
590    /**
591     * A frame initiated at class< enum or interface definition; holds instance variable names.
592     * @author Stephen Bloch
593     */
594    private static class ClassFrame extends AbstractFrame {
595        /** Set of idents of instance members declared in this frame. */
596        private final Set<DetailAST> instanceMembers;
597        /** Set of idents of instance methods declared in this frame. */
598        private final Set<DetailAST> instanceMethods;
599        /** Set of idents of variables declared in this frame. */
600        private final Set<DetailAST> staticMembers;
601        /** Set of idents of static methods declared in this frame. */
602        private final Set<DetailAST> staticMethods;
603
604        /**
605         * Creates new instance of ClassFrame.
606         * @param parent parent frame
607         * @param frameName frame name
608         */
609        ClassFrame(AbstractFrame parent, String frameName) {
610            super(parent, frameName);
611            instanceMembers = Sets.newHashSet();
612            instanceMethods = Sets.newHashSet();
613            staticMembers = Sets.newHashSet();
614            staticMethods = Sets.newHashSet();
615        }
616
617        @Override
618        protected FrameType getType() {
619            return FrameType.CLASS_FRAME;
620        }
621
622        /**
623         * Adds static member's ident.
624         * @param ident an ident of static member of the class
625         */
626        public void addStaticMember(final DetailAST ident) {
627            staticMembers.add(ident);
628        }
629
630        /**
631         * Adds static method's name.
632         * @param ident an ident of static method of the class
633         */
634        public void addStaticMethod(final DetailAST ident) {
635            staticMethods.add(ident);
636        }
637
638        /**
639         * Adds instance member's ident.
640         * @param ident an ident of instance member of the class
641         */
642        public void addInstanceMember(final DetailAST ident) {
643            instanceMembers.add(ident);
644        }
645
646        /**
647         * Adds instance method's name.
648         * @param ident an ident of instance method of the class
649         */
650        public void addInstanceMethod(final DetailAST ident) {
651            instanceMethods.add(ident);
652        }
653
654        /**
655         * Checks if a given name is a known instance member of the class.
656         * @param ident the IDENT ast of the name to check
657         * @return true is the given name is a name of a known
658         *         instance member of the class
659         */
660        public boolean hasInstanceMember(final DetailAST ident) {
661            return containsFieldOrVariableDef(instanceMembers, ident);
662        }
663
664        /**
665         * Checks if a given name is a known instance method of the class.
666         * @param ident the IDENT ast of the method call to check
667         * @return true if the given ast is correspondent to a known
668         *         instance method of the class
669         */
670        public boolean hasInstanceMethod(final DetailAST ident) {
671            return containsMethodDef(instanceMethods, ident);
672        }
673
674        /**
675         * Checks if a given name is a known static method of the class.
676         * @param ident the IDENT ast of the method call to check
677         * @return true is the given ast is correspondent to a known
678         *         instance method of the class
679         */
680        public boolean hasStaticMethod(final DetailAST ident) {
681            return containsMethodDef(staticMethods, ident);
682        }
683
684        @Override
685        protected boolean containsFieldOrVariable(DetailAST nameToFind) {
686            return containsFieldOrVariableDef(instanceMembers, nameToFind)
687                    || containsFieldOrVariableDef(staticMembers, nameToFind);
688        }
689
690        @Override
691        protected boolean isProperDefinition(DetailAST ident, DetailAST ast) {
692            final String nameToFind = ident.getText();
693            return nameToFind.equals(ast.getText());
694        }
695
696        @Override
697        protected AbstractFrame getIfContains(DetailAST nameToFind, boolean lookForMethod) {
698            AbstractFrame frame = null;
699
700            if (lookForMethod && containsMethod(nameToFind)
701                || containsFieldOrVariable(nameToFind)) {
702                frame = this;
703            }
704            else if (getParent() != null) {
705                frame = getParent().getIfContains(nameToFind, lookForMethod);
706            }
707            return frame;
708        }
709
710        /**
711         * Check whether the frame contains a given method.
712         * @param methodToFind the AST of the method to find.
713         * @return true, if a method with the same name and number of parameters is found.
714         */
715        private boolean containsMethod(DetailAST methodToFind) {
716            return containsMethodDef(instanceMethods, methodToFind)
717                || containsMethodDef(staticMethods, methodToFind);
718        }
719
720        /**
721         * Whether the set contains a method definition with the
722         *     same name and number of parameters.
723         * @param set the set of definitions.
724         * @param ident the specified method call IDENT ast.
725         * @return true if the set contains a definition with the
726         *     same name and number of parameters.
727         */
728        private boolean containsMethodDef(Set<DetailAST> set, DetailAST ident) {
729            boolean result = false;
730            for (DetailAST ast: set) {
731                if (isSimilarSignature(ident, ast)) {
732                    result = true;
733                    break;
734                }
735            }
736            return result;
737        }
738
739        /**
740         * Whether the method definition has the same name and number of parameters.
741         * @param ident the specified method call IDENT ast.
742         * @param ast the ast of a method definition to compare with.
743         * @return true if a method definition has the same name and number of parameters
744         *     as the method call.
745         */
746        private boolean isSimilarSignature(DetailAST ident, DetailAST ast) {
747            boolean result = false;
748            if (ident.getText().equals(ast.getText())) {
749                final int paramsNumber = ast.getParent().findFirstToken(TokenTypes.PARAMETERS)
750                    .getChildCount();
751                final int argsNumber = ident.getParent().findFirstToken(TokenTypes.ELIST)
752                    .getChildCount();
753                result = paramsNumber == argsNumber;
754            }
755            return result;
756        }
757    }
758
759    /**
760     * A frame initiated on entering a statement list; holds local variable names.
761     * @author Stephen Bloch
762     */
763    private static class BlockFrame extends AbstractFrame {
764
765        /**
766         * Creates block frame.
767         * @param parent parent frame
768         */
769        protected BlockFrame(AbstractFrame parent) {
770            super(parent, null);
771        }
772
773        @Override
774        protected FrameType getType() {
775            return FrameType.BLOCK_FRAME;
776        }
777    }
778}