1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.rop.cst;
18
19import com.android.dx.rop.type.Type;
20
21/**
22 * Constants of type {@code MethodHandle}.
23 */
24public final class CstMethodHandle extends TypedConstant {
25
26    public static final int METHOD_HANDLE_TYPE_STATIC_PUT = 0;
27    public static final int METHOD_HANDLE_TYPE_STATIC_GET = 1;
28    public static final int METHOD_HANDLE_TYPE_INSTANCE_PUT = 2;
29    public static final int METHOD_HANDLE_TYPE_INSTANCE_GET = 3;
30
31    public static final int METHOD_HANDLE_TYPE_INVOKE_STATIC = 4;
32    public static final int METHOD_HANDLE_TYPE_INVOKE_INSTANCE = 5;
33    public static final int METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR = 6;
34    public static final int METHOD_HANDLE_TYPE_INVOKE_DIRECT = 7;
35    public static final int METHOD_HANDLE_TYPE_INVOKE_INTERFACE = 8;
36
37    private static final String [] TYPE_NAMES = {
38        "static-put", "static-get", "instance-put", "instance-get",
39        "invoke-static", "invoke-instance", "invoke-constructor", "invoke-direct",
40        "invoke-interface"
41    };
42
43    /** The type of MethodHandle */
44    private final int type;
45
46    /** {@code non-null;} the referenced constant */
47    private final Constant ref;
48
49    /**
50     * Makes an instance for the given value. This may (but does not
51     * necessarily) return an already-allocated instance.
52     *
53     * @param type the type of this handle
54     * @param ref {@code non-null;} the referenced field or method constant
55     * @return {@code non-null;} the appropriate instance
56     */
57    public static CstMethodHandle make(int type, Constant ref) {
58        if (isAccessor(type)) {
59            if (!(ref instanceof CstFieldRef)) {
60                throw new IllegalArgumentException("ref has wrong type: " + ref.getClass());
61            }
62        } else if (isInvocation(type)) {
63            if (!(ref instanceof CstBaseMethodRef)) {
64                throw new IllegalArgumentException("ref has wrong type: " + ref.getClass());
65            }
66        } else {
67            throw new IllegalArgumentException("type is out of range: " + type);
68        }
69        return new CstMethodHandle(type, ref);
70    }
71
72    /**
73     * Constructs an instance. This constructor is private; use {@link #make}.
74     *
75     * @param type the type of this handle
76     * @param ref the actual referenced constant
77     */
78    private CstMethodHandle(int type, Constant ref) {
79        this.type = type;
80        this.ref = ref;
81    }
82
83    /**
84     * Gets the actual constant.
85     *
86     * @return the value
87     */
88    public Constant getRef() {
89        return ref;
90    }
91
92    /**
93     * Gets the type of this method handle.
94     *
95     * @return the type
96     */
97    public int getMethodHandleType() {
98        return type;
99    }
100
101    /**
102     * Reports whether the method handle type is a field accessor.
103     *
104     * @param type the method handle type
105     * @return true if the method handle type is a field accessor, false otherwise
106     */
107    public static boolean isAccessor(int type) {
108        switch (type) {
109            case METHOD_HANDLE_TYPE_STATIC_PUT:
110            case METHOD_HANDLE_TYPE_STATIC_GET:
111            case METHOD_HANDLE_TYPE_INSTANCE_PUT:
112            case METHOD_HANDLE_TYPE_INSTANCE_GET:
113                return true;
114            default:
115                return false;
116        }
117    }
118
119    /**
120     * Reports whether the method handle is a field accessor.
121     *
122     * @return true if the method handle is a field accessor, false otherwise
123     */
124    public boolean isAccessor() {
125        return isAccessor(type);
126    }
127
128    /**
129     * Reports whether the method handle type is a method invocation.
130     *
131     * @param type the method handle type
132     * @return true if the method handle type is a method invocation, false otherwise
133     */
134    public static boolean isInvocation(int type) {
135        switch (type) {
136            case METHOD_HANDLE_TYPE_INVOKE_STATIC:
137            case METHOD_HANDLE_TYPE_INVOKE_INSTANCE:
138            case METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR:
139            case METHOD_HANDLE_TYPE_INVOKE_DIRECT:
140            case METHOD_HANDLE_TYPE_INVOKE_INTERFACE:
141                return true;
142            default:
143                return false;
144        }
145    }
146
147    /**
148     * Reports whether the method handle is a method invocation.
149     *
150     * @return true if the method handle is a method invocation, false otherwise
151     */
152    public boolean isInvocation() {
153        return isInvocation(type);
154    }
155
156    /**
157     * Gets a human readable name for a method handle type.
158     *
159     * @param type the method handle type
160     * @return the string representation of the type
161     */
162    public static String getMethodHandleTypeName(final int type) {
163        return TYPE_NAMES[type];
164    }
165
166    /** {@inheritDoc} */
167    @Override
168    public boolean isCategory2() {
169        return false;
170    }
171
172    /** {@inheritDoc} */
173    @Override
174    protected int compareTo0(Constant other) {
175        CstMethodHandle otherHandle = (CstMethodHandle) other;
176        if (getMethodHandleType() == otherHandle.getMethodHandleType()) {
177            return getRef().compareTo(otherHandle.getRef());
178        } else {
179            return Integer.compare(getMethodHandleType(), otherHandle.getMethodHandleType());
180        }
181    }
182
183    /** {@inheritDoc} */
184    @Override
185    public String toString() {
186        return "method-handle{" + toHuman() + "}";
187    }
188
189    /** {@inheritDoc} */
190    @Override
191    public String typeName() {
192        return "method handle";
193    }
194
195    /** {@inheritDoc} */
196    @Override
197    public String toHuman() {
198        return getMethodHandleTypeName(type)+ "," + ref.toString();
199    }
200
201    @Override
202    public Type getType() {
203        return Type.METHOD_HANDLE;
204    }
205}
206