From 010b0fb998a7a564206cc7e09eef8026abc89823 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Thu, 20 Mar 2014 22:57:32 -0700 Subject: [PATCH] Add PsiModifierList implementation Also implement PsiModifierList-related methods in SmaliClass --- smalidea/src/main/antlr3/smalideaParser.g | 3 + .../jf/smalidea/psi/SmaliElementTypes.java | 1 + .../psi/iface/SmaliModifierListOwner.java | 47 +++++ .../jf/smalidea/psi/impl/SmaliAccessList.java | 14 ++ .../org/jf/smalidea/psi/impl/SmaliClass.java | 46 ++++- .../psi/impl/SmaliClassStatement.java | 5 + .../psi/impl/SmaliCompositeElement.java | 19 ++ .../smalidea/psi/impl/SmaliModifierList.java | 169 +++++++++++++++++ .../psi/stub/SmaliModifierListStub.java | 50 +++++ .../element/SmaliModifierListElementType.java | 83 +++++++++ .../smalidea/SmaliClassModifierListTest.java | 173 ++++++++++++++++++ 11 files changed, 603 insertions(+), 7 deletions(-) create mode 100644 smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java create mode 100644 smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java create mode 100644 smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java create mode 100644 smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java create mode 100644 smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java diff --git a/smalidea/src/main/antlr3/smalideaParser.g b/smalidea/src/main/antlr3/smalideaParser.g index a24782a6..76a13ac0 100644 --- a/smalidea/src/main/antlr3/smalideaParser.g +++ b/smalidea/src/main/antlr3/smalideaParser.g @@ -155,6 +155,9 @@ sync[boolean toEof] : /*epsilon*/; smali_file + @init { + mark().done(SmaliElementTypes.MODIFIER_LIST); + } : ( ( class_spec diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java index 00f05c6f..5d1cb493 100644 --- a/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java +++ b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java @@ -40,6 +40,7 @@ public class SmaliElementTypes { public static final SmaliFieldElementType FIELD = SmaliFieldElementType.INSTANCE; public static final SmaliMethodElementType METHOD = SmaliMethodElementType.INSTANCE; public static final SmaliAnnotationElementType ANNOTATION = SmaliAnnotationElementType.INSTANCE; + public static final SmaliModifierListElementType MODIFIER_LIST = SmaliModifierListElementType.INSTANCE; public static final SmaliCompositeElementType LITERAL = new SmaliCompositeElementType("LITERAL", SmaliLiteral.FACTORY); diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java b/smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java new file mode 100644 index 00000000..ca30b105 --- /dev/null +++ b/smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.smalidea.psi.iface; + +import com.intellij.psi.PsiAnnotationOwner; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jf.smalidea.psi.impl.SmaliAccessList; +import org.jf.smalidea.psi.impl.SmaliAnnotation; + +public interface SmaliModifierListOwner extends PsiAnnotationOwner { + @Nullable SmaliAccessList getAccessFlagsNode(); + @NotNull @Override SmaliAnnotation[] getAnnotations(); + @NotNull @Override SmaliAnnotation[] getApplicableAnnotations(); + @Nullable @Override SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName); + @NotNull @Override SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName); +} diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAccessList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAccessList.java index 6330fe1d..c25cf67c 100644 --- a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAccessList.java +++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAccessList.java @@ -31,6 +31,10 @@ package org.jf.smalidea.psi.impl; +import com.intellij.lang.ASTNode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jf.smalidea.SmaliTokens; import org.jf.smalidea.psi.SmaliCompositeElementFactory; import org.jf.smalidea.psi.SmaliElementTypes; @@ -44,4 +48,14 @@ public class SmaliAccessList extends SmaliCompositeElement { public SmaliAccessList() { super(SmaliElementTypes.ACCESS_LIST); } + + @Nullable + public ASTNode getAccessFlagNode(@NotNull String accessFlag) { + for (ASTNode node: findChildrenByType(SmaliTokens.ACCESS_SPEC)) { + if (node.getText().equals(accessFlag)) { + return node; + } + } + return null; + } } diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java index 6d9d6c87..dd2b5a25 100644 --- a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java +++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java @@ -41,12 +41,13 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jf.smalidea.psi.SmaliElementTypes; +import org.jf.smalidea.psi.iface.SmaliModifierListOwner; import org.jf.smalidea.psi.stub.SmaliClassStub; import java.util.Collection; import java.util.List; -public class SmaliClass extends SmaliStubBasedPsiElement implements PsiClass { +public class SmaliClass extends SmaliStubBasedPsiElement implements PsiClass, SmaliModifierListOwner { public SmaliClass(@NotNull SmaliClassStub stub) { super(stub, SmaliElementTypes.CLASS); } @@ -55,6 +56,13 @@ public class SmaliClass extends SmaliStubBasedPsiElement impleme super(node); } + @Nullable @Override public SmaliAccessList getAccessFlagsNode() { + SmaliClassStatement classStatement = findChildByClass(SmaliClassStatement.class); + if (classStatement == null) { + return null; + } + return classStatement.getAccessFlagsNode(); + } @Override public boolean hasTypeParameters() { // TODO: implement generics return false; @@ -81,15 +89,15 @@ public class SmaliClass extends SmaliStubBasedPsiElement impleme } @Override public boolean isInterface() { - return false; + return getModifierList().hasModifierProperty("interface"); } @Override public boolean isAnnotationType() { - return false; + return getModifierList().hasModifierProperty("annotation"); } @Override public boolean isEnum() { - return false; + return getModifierList().hasModifierProperty("enum"); } @Nullable @Override public PsiReferenceList getExtendsList() { @@ -237,11 +245,35 @@ public class SmaliClass extends SmaliStubBasedPsiElement impleme return new PsiTypeParameter[0]; } - @Nullable @Override public PsiModifierList getModifierList() { - return null; + @NotNull @Override public SmaliModifierList getModifierList() { + SmaliModifierList modifierList = getStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST); + assert modifierList != null; + return modifierList; } @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) { - return false; + return getModifierList().hasModifierProperty(name); + } + + @NotNull @Override public SmaliAnnotation[] getAnnotations() { + return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]); + } + + @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() { + return getAnnotations(); + } + + @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { + for (SmaliAnnotation annotation: getAnnotations()) { + if (qualifiedName.equals(annotation.getQualifiedName())) { + return annotation; + } + } + return null; + } + + @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { + // TODO: implement this + return null; } } diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java index baf254fd..56c6e295 100644 --- a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java +++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java @@ -57,4 +57,9 @@ public class SmaliClassStatement extends SmaliCompositeElement { } return classType.getJavaType(); } + + @Nullable + public SmaliAccessList getAccessFlagsNode() { + return findChildByClass(SmaliAccessList.class); + } } diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java index a7496113..ca46c142 100644 --- a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java +++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java @@ -31,6 +31,8 @@ package org.jf.smalidea.psi.impl; +import com.google.common.collect.ImmutableList; +import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.source.tree.CompositePsiElement; import com.intellij.psi.tree.IElementType; @@ -47,6 +49,23 @@ public abstract class SmaliCompositeElement extends CompositePsiElement { super(type); } + @NotNull + @SuppressWarnings("unchecked") + protected List findChildrenByType(IElementType elementType) { + List result = ImmutableList.of(); + ASTNode child = getNode().getFirstChildNode(); + while (child != null) { + if (elementType == child.getElementType()) { + if (result.size() == 0) { + result = new ArrayList(); + } + result.add((ASTNode)child.getPsi()); + } + child = child.getTreeNext(); + } + return result; + } + @NotNull @SuppressWarnings("unchecked") protected T[] findChildrenByClass(Class aClass) { diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java new file mode 100644 index 00000000..15e233c4 --- /dev/null +++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java @@ -0,0 +1,169 @@ +/* + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.smalidea.psi.impl; + +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiModifier.ModifierConstant; +import com.intellij.psi.PsiModifierList; +import com.intellij.psi.PsiModifierListOwner; +import com.intellij.psi.StubBasedPsiElement; +import com.intellij.psi.impl.source.tree.Factory; +import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.psi.tree.TokenSet; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jf.dexlib2.AccessFlags; +import org.jf.smalidea.SmaliTokens; +import org.jf.smalidea.psi.iface.SmaliModifierListOwner; +import org.jf.smalidea.psi.stub.SmaliModifierListStub; +import org.jf.smalidea.psi.stub.element.SmaliModifierListElementType; + +import javax.annotation.Nonnull; + +public class SmaliModifierList extends SmaliStubBasedPsiElement + implements StubBasedPsiElement, PsiModifierList { + public SmaliModifierList(@NotNull ASTNode node) { + super(node); + } + + public SmaliModifierList(@NotNull SmaliModifierListStub stub) { + super(stub, SmaliModifierListElementType.INSTANCE); + } + + @Nullable + private SmaliAccessList findAccessListNode() { + return ((SmaliModifierListOwner)getParent()).getAccessFlagsNode(); + } + + public int getAccessFlags() { + SmaliModifierListStub stub = getStub(); + if (stub != null) { + return stub.getAccessFlags(); + } + + ASTNode accessListNode = findAccessListNode(); + if (accessListNode == null) { + return 0; + } + + int flags = 0; + for (ASTNode accessSpecNode: accessListNode.getChildren(TokenSet.create(SmaliTokens.ACCESS_SPEC))) { + AccessFlags flag = AccessFlags.getAccessFlag(accessSpecNode.getText()); + if (flag != null) { + flags |= flag.getValue(); + } + } + + return flags; + } + + @Override public boolean hasModifierProperty(@ModifierConstant @NotNull @NonNls String name) { + return hasExplicitModifier(name); + } + + @Override public boolean hasExplicitModifier(@ModifierConstant @NotNull @NonNls String name) { + SmaliModifierListStub stub = getStub(); + if (stub != null) { + AccessFlags flag = AccessFlags.getAccessFlag(name); + if (flag == null) { + return false; + } + return (stub.getAccessFlags() & flag.getValue()) != 0; + } + + ASTNode accessListNode = findAccessListNode(); + if (accessListNode == null) { + return false; + } + + for (ASTNode accessSpecNode: accessListNode.getChildren(TokenSet.create(SmaliTokens.ACCESS_SPEC))) { + if (accessSpecNode.getText().equals(name)) { + return true; + } + } + + return false; + } + + @Override + public void setModifierProperty(@ModifierConstant @NotNull @NonNls String name, boolean addModifier) + throws IncorrectOperationException { + if (addModifier) { + SmaliAccessList accessListNode = findAccessListNode(); + if (accessListNode == null) { + throw new IncorrectOperationException("Cannot add modifier: no .class statement"); + } + TreeElement leaf = Factory.createSingleLeafElement(SmaliTokens.ACCESS_SPEC, name, null, getManager()); + accessListNode.addInternal(leaf, leaf, null, null); + } else { + SmaliAccessList accessListNode = findAccessListNode(); + if (accessListNode == null) { + return; + } + + ASTNode accessSpec = accessListNode.getAccessFlagNode(name); + if (accessSpec != null) { + accessSpec.getPsi().delete(); + } + } + } + + @Override + public void checkSetModifierProperty(@ModifierConstant @NotNull @NonNls String name, boolean addModifier) + throws IncorrectOperationException { + } + + @Nonnull + private SmaliModifierListOwner getParentForAnnotations() { + SmaliModifierListOwner parent = (SmaliModifierListOwner)getStubOrPsiParentOfType(PsiModifierListOwner.class); + assert parent != null; + return parent; + } + + @NotNull @Override public SmaliAnnotation[] getAnnotations() { + return getParentForAnnotations().getAnnotations(); + } + + @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() { + return getParentForAnnotations().getApplicableAnnotations(); + } + + @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) { + return getParentForAnnotations().findAnnotation(qualifiedName); + } + + @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) { + return getParentForAnnotations().addAnnotation(qualifiedName); + } +} diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java new file mode 100644 index 00000000..e0a5f0e4 --- /dev/null +++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.smalidea.psi.stub; + +import com.intellij.psi.stubs.StubBase; +import com.intellij.psi.stubs.StubElement; +import org.jf.smalidea.psi.impl.SmaliMethod; +import org.jf.smalidea.psi.stub.element.SmaliModifierListElementType; + +public class SmaliModifierListStub extends StubBase { + private final int accessFlags; + + public SmaliModifierListStub(StubElement parent, int accessFlags) { + super(parent, SmaliModifierListElementType.INSTANCE); + this.accessFlags = accessFlags; + } + + public int getAccessFlags() { + return accessFlags; + } +} diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java new file mode 100644 index 00000000..ae6f90e0 --- /dev/null +++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java @@ -0,0 +1,83 @@ +/* + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.smalidea.psi.stub.element; + +import com.intellij.lang.ASTNode; +import com.intellij.psi.stubs.IndexSink; +import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.stubs.StubInputStream; +import com.intellij.psi.stubs.StubOutputStream; +import org.jetbrains.annotations.NotNull; +import org.jf.smalidea.psi.impl.SmaliModifierList; +import org.jf.smalidea.psi.stub.SmaliModifierListStub; +import org.jf.smalidea.psi.stub.SmaliStubElementType; + +import java.io.IOException; + +public class SmaliModifierListElementType extends SmaliStubElementType { + public static final SmaliModifierListElementType INSTANCE = new SmaliModifierListElementType(); + + private SmaliModifierListElementType() { + super("MODIFIER_LIST"); + } + + @NotNull @Override public String getExternalId() { + return "smali.modifier_list"; + } + + @Override public SmaliModifierList createPsi(@NotNull SmaliModifierListStub stub) { + return new SmaliModifierList(stub); + } + + @Override public SmaliModifierList createPsi(@NotNull ASTNode node) { + return new SmaliModifierList(node); + } + + @Override public SmaliModifierListStub createStub(@NotNull SmaliModifierList psi, StubElement parentStub) { + return new SmaliModifierListStub(parentStub, psi.getAccessFlags()); + } + + @Override + public void serialize(@NotNull SmaliModifierListStub stub, @NotNull StubOutputStream dataStream) + throws IOException { + dataStream.writeVarInt(stub.getAccessFlags()); + } + + @NotNull @Override + public SmaliModifierListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) + throws IOException { + return new SmaliModifierListStub(parentStub, dataStream.readVarInt()); + } + + @Override public void indexStub(@NotNull SmaliModifierListStub stub, @NotNull IndexSink sink) { + } +} diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java new file mode 100644 index 00000000..d88f5bf3 --- /dev/null +++ b/smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2014, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.smalidea; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import junit.framework.Assert; +import org.jf.dexlib2.AccessFlags; +import org.jf.smalidea.psi.impl.SmaliAnnotation; +import org.jf.smalidea.psi.impl.SmaliClass; +import org.jf.smalidea.psi.impl.SmaliFile; +import org.jf.smalidea.psi.impl.SmaliModifierList; + +public class SmaliClassModifierListTest extends LightCodeInsightFixtureTestCase { + public void testAllClassAccessFlags() { + SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", + ".class public final interface abstract synthetic enum annotation Lmy/pkg/blah; " + + ".super Ljava/lang/Object;"); + + SmaliClass smaliClass = file.getPsiClass(); + SmaliModifierList modifierList = smaliClass.getModifierList(); + + Assert.assertEquals(AccessFlags.PUBLIC.getValue() | + AccessFlags.FINAL.getValue() | + AccessFlags.INTERFACE.getValue() | + AccessFlags.ABSTRACT.getValue() | + AccessFlags.SYNTHETIC.getValue() | + AccessFlags.ENUM.getValue() | + AccessFlags.ANNOTATION.getValue(), + modifierList.getAccessFlags()); + + Assert.assertTrue(modifierList.hasModifierProperty("public")); + Assert.assertTrue(modifierList.hasModifierProperty("final")); + Assert.assertTrue(modifierList.hasModifierProperty("interface")); + Assert.assertTrue(modifierList.hasModifierProperty("abstract")); + Assert.assertTrue(modifierList.hasModifierProperty("synthetic")); + Assert.assertTrue(modifierList.hasModifierProperty("enum")); + Assert.assertTrue(modifierList.hasModifierProperty("annotation")); + + Assert.assertTrue(modifierList.hasExplicitModifier("public")); + Assert.assertTrue(modifierList.hasExplicitModifier("final")); + Assert.assertTrue(modifierList.hasExplicitModifier("interface")); + Assert.assertTrue(modifierList.hasExplicitModifier("abstract")); + Assert.assertTrue(modifierList.hasExplicitModifier("synthetic")); + Assert.assertTrue(modifierList.hasExplicitModifier("enum")); + Assert.assertTrue(modifierList.hasExplicitModifier("annotation")); + } + + public void testNoClassAccessFlags() { + SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", + ".class Lmy/pkg/blah; " + + ".super Ljava/lang/Object;"); + + SmaliClass smaliClass = file.getPsiClass(); + SmaliModifierList modifierList = smaliClass.getModifierList(); + + Assert.assertEquals(0, modifierList.getAccessFlags()); + + Assert.assertFalse(modifierList.hasModifierProperty("public")); + Assert.assertFalse(modifierList.hasModifierProperty("final")); + Assert.assertFalse(modifierList.hasModifierProperty("interface")); + Assert.assertFalse(modifierList.hasModifierProperty("abstract")); + Assert.assertFalse(modifierList.hasModifierProperty("synthetic")); + Assert.assertFalse(modifierList.hasModifierProperty("enum")); + Assert.assertFalse(modifierList.hasModifierProperty("annotation")); + + Assert.assertFalse(modifierList.hasExplicitModifier("public")); + Assert.assertFalse(modifierList.hasExplicitModifier("final")); + Assert.assertFalse(modifierList.hasExplicitModifier("interface")); + Assert.assertFalse(modifierList.hasExplicitModifier("abstract")); + Assert.assertFalse(modifierList.hasExplicitModifier("synthetic")); + Assert.assertFalse(modifierList.hasExplicitModifier("enum")); + Assert.assertFalse(modifierList.hasExplicitModifier("annotation")); + } + + public void testAddClassAccessFlag() { + final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", + ".class public Lmy/pkg/blah;\n" + + ".super Ljava/lang/Object;"); + myFixture.configureFromExistingVirtualFile(file.getVirtualFile()); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { + file.getPsiClass().getModifierList().setModifierProperty("final", true); + } + }); + + myFixture.checkResult( + ".class public final Lmy/pkg/blah;\n" + + ".super Ljava/lang/Object;"); + } + + public void testRemoveClassAccessFlag() { + final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", + ".class public final Lmy/pkg/blah;\n" + + ".super Ljava/lang/Object;"); + myFixture.configureFromExistingVirtualFile(file.getVirtualFile()); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { + file.getPsiClass().getModifierList().setModifierProperty("final", false); + } + }); + + myFixture.checkResult( + ".class public Lmy/pkg/blah;\n" + + ".super Ljava/lang/Object;"); + } + + public void testBasicAnnotation() { + final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", + ".class public final Lmy/pkg/blah;\n" + + ".super Ljava/lang/Object;\n" + + ".annotation Lmy/pkg/anno; .end annotation"); + + SmaliClass smaliClass = file.getPsiClass(); + SmaliModifierList modifierList = smaliClass.getModifierList(); + + SmaliAnnotation[] annotations = modifierList.getAnnotations(); + Assert.assertEquals(1, annotations.length); + + // TODO: use PsiAnnotation.getQualifiedName() instead, once it has been implemented + Assert.assertEquals(".annotation Lmy/pkg/anno; .end annotation", annotations[0].getText()); + + SmaliAnnotation[] applicableAnnotations = modifierList.getApplicableAnnotations(); + Assert.assertEquals(1, applicableAnnotations.length); + Assert.assertEquals(annotations[0], applicableAnnotations[0]); + } + + public void testNoAnnotation() { + final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", + ".class public final Lmy/pkg/blah;\n" + + ".super Ljava/lang/Object;"); + + SmaliClass smaliClass = file.getPsiClass(); + SmaliModifierList modifierList = smaliClass.getModifierList(); + + Assert.assertEquals(0, modifierList.getAnnotations().length); + Assert.assertEquals(0, modifierList.getApplicableAnnotations().length); + } + + // TODO: test modifierList.findAnnotation once PsiAnnotation.getQualifiedName has been implemented + // TODO: test modifierList.addAnnotation once implemented +}