001/*
002 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.hotspot;
024
025import static jdk.internal.jvmci.common.UnsafeAccess.*;
026import static jdk.internal.jvmci.hotspot.HotSpotJVMCIRuntime.*;
027import static jdk.internal.jvmci.hotspot.HotSpotResolvedJavaMethodImpl.Options.*;
028
029import java.lang.annotation.*;
030import java.lang.reflect.*;
031import java.util.*;
032
033import jdk.internal.jvmci.common.*;
034import jdk.internal.jvmci.meta.*;
035import jdk.internal.jvmci.options.*;
036
037/**
038 * Implementation of {@link JavaMethod} for resolved HotSpot methods.
039 */
040public final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, HotSpotProxified, MethodIdHolder {
041
042    public static class Options {
043        // @formatter:off
044        @Option(help = "", type = OptionType.Debug)
045        public static final OptionValue<Boolean> UseProfilingInformation = new OptionValue<>(true);
046        // @formatter:on
047    }
048
049    /**
050     * Reference to metaspace Method object.
051     */
052    private final long metaspaceMethod;
053
054    private final HotSpotResolvedObjectTypeImpl holder;
055    private final HotSpotConstantPool constantPool;
056    private final HotSpotSignature signature;
057    private HotSpotMethodData methodData;
058    private byte[] code;
059    private Member toJavaCache;
060
061    /**
062     * Gets the holder of a HotSpot metaspace method native object.
063     *
064     * @param metaspaceMethod a metaspace Method object
065     * @return the {@link ResolvedJavaType} corresponding to the holder of the
066     *         {@code metaspaceMethod}
067     */
068    public static HotSpotResolvedObjectTypeImpl getHolder(long metaspaceMethod) {
069        HotSpotVMConfig config = runtime().getConfig();
070        final long metaspaceConstMethod = unsafe.getAddress(metaspaceMethod + config.methodConstMethodOffset);
071        final long metaspaceConstantPool = unsafe.getAddress(metaspaceConstMethod + config.constMethodConstantsOffset);
072        final long metaspaceKlass = unsafe.getAddress(metaspaceConstantPool + config.constantPoolHolderOffset);
073        return HotSpotResolvedObjectTypeImpl.fromMetaspaceKlass(metaspaceKlass);
074    }
075
076    /**
077     * Gets the {@link ResolvedJavaMethod} for a HotSpot metaspace method native object.
078     *
079     * @param metaspaceMethod a metaspace Method object
080     * @return the {@link ResolvedJavaMethod} corresponding to {@code metaspaceMethod}
081     */
082    public static HotSpotResolvedJavaMethod fromMetaspace(long metaspaceMethod) {
083        HotSpotResolvedObjectTypeImpl holder = getHolder(metaspaceMethod);
084        return holder.createMethod(metaspaceMethod);
085    }
086
087    public HotSpotResolvedJavaMethodImpl(HotSpotResolvedObjectTypeImpl holder, long metaspaceMethod) {
088        // It would be too much work to get the method name here so we fill it in later.
089        super(null);
090        this.metaspaceMethod = metaspaceMethod;
091        this.holder = holder;
092
093        HotSpotVMConfig config = runtime().getConfig();
094        final long constMethod = getConstMethod();
095
096        /*
097         * Get the constant pool from the metaspace method. Some methods (e.g. intrinsics for
098         * signature-polymorphic method handle methods) have their own constant pool instead of the
099         * one from their holder.
100         */
101        final long metaspaceConstantPool = unsafe.getAddress(constMethod + config.constMethodConstantsOffset);
102        this.constantPool = new HotSpotConstantPool(metaspaceConstantPool);
103
104        final int nameIndex = unsafe.getChar(constMethod + config.constMethodNameIndexOffset);
105        this.name = constantPool.lookupUtf8(nameIndex);
106
107        final int signatureIndex = unsafe.getChar(constMethod + config.constMethodSignatureIndexOffset);
108        this.signature = (HotSpotSignature) constantPool.lookupSignature(signatureIndex);
109    }
110
111    /**
112     * Returns a pointer to this method's constant method data structure (
113     * {@code Method::_constMethod}).
114     *
115     * @return pointer to this method's ConstMethod
116     */
117    private long getConstMethod() {
118        assert metaspaceMethod != 0;
119        return unsafe.getAddress(metaspaceMethod + runtime().getConfig().methodConstMethodOffset);
120    }
121
122    @Override
123    public boolean equals(Object obj) {
124        if (obj instanceof HotSpotResolvedJavaMethodImpl) {
125            HotSpotResolvedJavaMethodImpl that = (HotSpotResolvedJavaMethodImpl) obj;
126            return that.metaspaceMethod == metaspaceMethod;
127        }
128        return false;
129    }
130
131    @Override
132    public int hashCode() {
133        return (int) metaspaceMethod;
134    }
135
136    /**
137     * Returns this method's flags ({@code Method::_flags}).
138     *
139     * @return flags of this method
140     */
141    private int getFlags() {
142        return unsafe.getByte(metaspaceMethod + runtime().getConfig().methodFlagsOffset);
143    }
144
145    /**
146     * Returns this method's constant method flags ({@code ConstMethod::_flags}).
147     *
148     * @return flags of this method's ConstMethod
149     */
150    private int getConstMethodFlags() {
151        return unsafe.getChar(getConstMethod() + runtime().getConfig().constMethodFlagsOffset);
152    }
153
154    @Override
155    public HotSpotResolvedObjectTypeImpl getDeclaringClass() {
156        return holder;
157    }
158
159    /**
160     * Gets the address of the C++ Method object for this method.
161     */
162    public JavaConstant getMetaspaceMethodConstant() {
163        return HotSpotMetaspaceConstantImpl.forMetaspaceObject(getHostWordKind(), metaspaceMethod, this, false);
164    }
165
166    public long getMetaspaceMethod() {
167        return metaspaceMethod;
168    }
169
170    @Override
171    public JavaConstant getEncoding() {
172        return getMetaspaceMethodConstant();
173    }
174
175    /**
176     * Gets the complete set of modifiers for this method which includes the JVM specification
177     * modifiers as well as the HotSpot internal modifiers.
178     */
179    public int getAllModifiers() {
180        return unsafe.getInt(metaspaceMethod + runtime().getConfig().methodAccessFlagsOffset);
181    }
182
183    @Override
184    public int getModifiers() {
185        return getAllModifiers() & ModifiersProvider.jvmMethodModifiers();
186    }
187
188    @Override
189    public boolean canBeStaticallyBound() {
190        return (isFinal() || isPrivate() || isStatic() || holder.isLeaf()) && isConcrete();
191    }
192
193    @Override
194    public byte[] getCode() {
195        if (getCodeSize() == 0) {
196            return null;
197        }
198        if (code == null && holder.isLinked()) {
199            code = runtime().getCompilerToVM().getBytecode(metaspaceMethod);
200            assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length;
201        }
202        return code;
203    }
204
205    @Override
206    public int getCodeSize() {
207        return unsafe.getChar(getConstMethod() + runtime().getConfig().constMethodCodeSizeOffset);
208    }
209
210    @Override
211    public ExceptionHandler[] getExceptionHandlers() {
212        final boolean hasExceptionTable = (getConstMethodFlags() & runtime().getConfig().constMethodHasExceptionTable) != 0;
213        if (!hasExceptionTable) {
214            return new ExceptionHandler[0];
215        }
216
217        HotSpotVMConfig config = runtime().getConfig();
218        final int exceptionTableLength = runtime().getCompilerToVM().exceptionTableLength(metaspaceMethod);
219        ExceptionHandler[] handlers = new ExceptionHandler[exceptionTableLength];
220        long exceptionTableElement = runtime().getCompilerToVM().exceptionTableStart(metaspaceMethod);
221
222        for (int i = 0; i < exceptionTableLength; i++) {
223            final int startPc = unsafe.getChar(exceptionTableElement + config.exceptionTableElementStartPcOffset);
224            final int endPc = unsafe.getChar(exceptionTableElement + config.exceptionTableElementEndPcOffset);
225            final int handlerPc = unsafe.getChar(exceptionTableElement + config.exceptionTableElementHandlerPcOffset);
226            int catchTypeIndex = unsafe.getChar(exceptionTableElement + config.exceptionTableElementCatchTypeIndexOffset);
227
228            JavaType catchType;
229            if (catchTypeIndex == 0) {
230                catchType = null;
231            } else {
232                final int opcode = -1;  // opcode is not used
233                catchType = constantPool.lookupType(catchTypeIndex, opcode);
234
235                // Check for Throwable which catches everything.
236                if (catchType instanceof HotSpotResolvedObjectTypeImpl) {
237                    HotSpotResolvedObjectTypeImpl resolvedType = (HotSpotResolvedObjectTypeImpl) catchType;
238                    if (resolvedType.mirror() == Throwable.class) {
239                        catchTypeIndex = 0;
240                        catchType = null;
241                    }
242                }
243            }
244            handlers[i] = new ExceptionHandler(startPc, endPc, handlerPc, catchTypeIndex, catchType);
245
246            // Go to the next ExceptionTableElement
247            exceptionTableElement += config.exceptionTableElementSize;
248        }
249
250        return handlers;
251    }
252
253    /**
254     * Returns true if this method has a {@code CallerSensitive} annotation.
255     *
256     * @return true if CallerSensitive annotation present, false otherwise
257     */
258    public boolean isCallerSensitive() {
259        return (getFlags() & runtime().getConfig().methodFlagsCallerSensitive) != 0;
260    }
261
262    /**
263     * Returns true if this method has a {@code ForceInline} annotation.
264     *
265     * @return true if ForceInline annotation present, false otherwise
266     */
267    public boolean isForceInline() {
268        return (getFlags() & runtime().getConfig().methodFlagsForceInline) != 0;
269    }
270
271    /**
272     * Returns true if this method has a {@code DontInline} annotation.
273     *
274     * @return true if DontInline annotation present, false otherwise
275     */
276    public boolean isDontInline() {
277        return (getFlags() & runtime().getConfig().methodFlagsDontInline) != 0;
278    }
279
280    /**
281     * Manually adds a DontInline annotation to this method.
282     */
283    public void setNotInlineable() {
284        runtime().getCompilerToVM().doNotInlineOrCompile(metaspaceMethod);
285    }
286
287    /**
288     * Returns true if this method is one of the special methods that is ignored by security stack
289     * walks.
290     *
291     * @return true if special method ignored by security stack walks, false otherwise
292     */
293    public boolean ignoredBySecurityStackWalk() {
294        return runtime().getCompilerToVM().methodIsIgnoredBySecurityStackWalk(metaspaceMethod);
295    }
296
297    public boolean hasBalancedMonitors() {
298        HotSpotVMConfig config = runtime().getConfig();
299        final int modifiers = getAllModifiers();
300
301        // Method has no monitorenter/exit bytecodes.
302        if ((modifiers & config.jvmAccHasMonitorBytecodes) == 0) {
303            return false;
304        }
305
306        // Check to see if a previous compilation computed the monitor-matching analysis.
307        if ((modifiers & config.jvmAccMonitorMatch) != 0) {
308            return true;
309        }
310
311        // This either happens only once if monitors are balanced or very rarely multiple-times.
312        return runtime().getCompilerToVM().hasBalancedMonitors(metaspaceMethod);
313    }
314
315    @Override
316    public boolean isClassInitializer() {
317        return "<clinit>".equals(name) && isStatic();
318    }
319
320    @Override
321    public boolean isConstructor() {
322        return "<init>".equals(name) && !isStatic();
323    }
324
325    @Override
326    public int getMaxLocals() {
327        if (isAbstract() || isNative()) {
328            return 0;
329        }
330        HotSpotVMConfig config = runtime().getConfig();
331        return unsafe.getChar(getConstMethod() + config.methodMaxLocalsOffset);
332    }
333
334    @Override
335    public int getMaxStackSize() {
336        if (isAbstract() || isNative()) {
337            return 0;
338        }
339        HotSpotVMConfig config = runtime().getConfig();
340        return config.extraStackEntries + unsafe.getChar(getConstMethod() + config.constMethodMaxStackOffset);
341    }
342
343    @Override
344    public StackTraceElement asStackTraceElement(int bci) {
345        if (bci < 0 || bci >= getCodeSize()) {
346            // HotSpot code can only construct stack trace elements for valid bcis
347            StackTraceElement ste = runtime().getCompilerToVM().getStackTraceElement(metaspaceMethod, 0);
348            return new StackTraceElement(ste.getClassName(), ste.getMethodName(), ste.getFileName(), -1);
349        }
350        return runtime().getCompilerToVM().getStackTraceElement(metaspaceMethod, bci);
351    }
352
353    public ResolvedJavaMethod uniqueConcreteMethod(HotSpotResolvedObjectType receiver) {
354        if (receiver.isInterface()) {
355            // Cannot trust interfaces. Because of:
356            // interface I { void foo(); }
357            // class A { public void foo() {} }
358            // class B extends A implements I { }
359            // class C extends B { public void foo() { } }
360            // class D extends B { }
361            // Would lead to identify C.foo() as the unique concrete method for I.foo() without
362            // seeing A.foo().
363            return null;
364        }
365        long metaspaceKlass = ((HotSpotResolvedObjectTypeImpl) receiver).getMetaspaceKlass();
366        final long uniqueConcreteMethod = runtime().getCompilerToVM().findUniqueConcreteMethod(metaspaceKlass, metaspaceMethod);
367        if (uniqueConcreteMethod == 0) {
368            return null;
369        }
370        return fromMetaspace(uniqueConcreteMethod);
371    }
372
373    @Override
374    public HotSpotSignature getSignature() {
375        return signature;
376    }
377
378    /**
379     * Gets the value of {@code Method::_code}.
380     *
381     * @return the value of {@code Method::_code}
382     */
383    private long getCompiledCode() {
384        HotSpotVMConfig config = runtime().getConfig();
385        return unsafe.getAddress(metaspaceMethod + config.methodCodeOffset);
386    }
387
388    /**
389     * Returns whether this method has compiled code.
390     *
391     * @return true if this method has compiled code, false otherwise
392     */
393    public boolean hasCompiledCode() {
394        return getCompiledCode() != 0L;
395    }
396
397    /**
398     * @param level
399     * @return true if the currently installed code was generated at {@code level}.
400     */
401    public boolean hasCompiledCodeAtLevel(int level) {
402        long compiledCode = getCompiledCode();
403        if (compiledCode != 0) {
404            return unsafe.getInt(compiledCode + runtime().getConfig().nmethodCompLevelOffset) == level;
405        }
406        return false;
407    }
408
409    private static final String TraceMethodDataFilter = System.getProperty("jvmci.traceMethodDataFilter");
410
411    @Override
412    public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) {
413        ProfilingInfo info;
414
415        if (UseProfilingInformation.getValue() && methodData == null) {
416            long metaspaceMethodData = unsafe.getAddress(metaspaceMethod + runtime().getConfig().methodDataOffset);
417            if (metaspaceMethodData != 0) {
418                methodData = new HotSpotMethodData(metaspaceMethodData);
419                if (TraceMethodDataFilter != null && this.format("%H.%n").contains(TraceMethodDataFilter)) {
420                    System.out.println("Raw method data for " + this.format("%H.%n(%p)") + ":");
421                    System.out.println(methodData.toString());
422                }
423            }
424        }
425
426        if (methodData == null || (!methodData.hasNormalData() && !methodData.hasExtraData())) {
427            // Be optimistic and return false for exceptionSeen. A methodDataOop is allocated in
428            // case of a deoptimization.
429            info = DefaultProfilingInfo.get(TriState.FALSE);
430        } else {
431            info = new HotSpotProfilingInfo(methodData, this, includeNormal, includeOSR);
432        }
433        return info;
434    }
435
436    @Override
437    public void reprofile() {
438        runtime().getCompilerToVM().reprofile(metaspaceMethod);
439    }
440
441    @Override
442    public ConstantPool getConstantPool() {
443        return constantPool;
444    }
445
446    @Override
447    public Annotation[][] getParameterAnnotations() {
448        if (isConstructor()) {
449            Constructor<?> javaConstructor = toJavaConstructor();
450            return javaConstructor == null ? null : javaConstructor.getParameterAnnotations();
451        }
452        Method javaMethod = toJava();
453        return javaMethod == null ? null : javaMethod.getParameterAnnotations();
454    }
455
456    @Override
457    public Annotation[] getAnnotations() {
458        if (isConstructor()) {
459            Constructor<?> javaConstructor = toJavaConstructor();
460            return javaConstructor == null ? new Annotation[0] : javaConstructor.getAnnotations();
461        }
462        Method javaMethod = toJava();
463        return javaMethod == null ? new Annotation[0] : javaMethod.getAnnotations();
464    }
465
466    @Override
467    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
468        if (isConstructor()) {
469            Constructor<?> javaConstructor = toJavaConstructor();
470            return javaConstructor == null ? null : javaConstructor.getAnnotation(annotationClass);
471        }
472        Method javaMethod = toJava();
473        return javaMethod == null ? null : javaMethod.getAnnotation(annotationClass);
474    }
475
476    public boolean isDefault() {
477        if (isConstructor()) {
478            return false;
479        }
480        // Copied from java.lang.Method.isDefault()
481        int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
482        return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
483    }
484
485    @Override
486    public Type[] getGenericParameterTypes() {
487        if (isConstructor()) {
488            Constructor<?> javaConstructor = toJavaConstructor();
489            return javaConstructor == null ? null : javaConstructor.getGenericParameterTypes();
490        }
491        Method javaMethod = toJava();
492        return javaMethod == null ? null : javaMethod.getGenericParameterTypes();
493    }
494
495    public Class<?>[] signatureToTypes() {
496        Signature sig = getSignature();
497        int count = sig.getParameterCount(false);
498        Class<?>[] result = new Class<?>[count];
499        for (int i = 0; i < result.length; ++i) {
500            JavaType parameterType = sig.getParameterType(i, holder);
501            HotSpotResolvedJavaType resolvedParameterType = (HotSpotResolvedJavaType) parameterType.resolve(holder);
502            result[i] = resolvedParameterType.mirror();
503        }
504        return result;
505    }
506
507    private Method toJava() {
508        if (toJavaCache != null) {
509            return (Method) toJavaCache;
510        }
511        try {
512            Method result = holder.mirror().getDeclaredMethod(name, signatureToTypes());
513            toJavaCache = result;
514            return result;
515        } catch (NoSuchMethodException | NoClassDefFoundError e) {
516            return null;
517        }
518    }
519
520    private Constructor<?> toJavaConstructor() {
521        if (toJavaCache != null) {
522            return (Constructor<?>) toJavaCache;
523        }
524        try {
525            Constructor<?> result = holder.mirror().getDeclaredConstructor(signatureToTypes());
526            toJavaCache = result;
527            return result;
528        } catch (NoSuchMethodException | NoClassDefFoundError e) {
529            return null;
530        }
531    }
532
533    @Override
534    public boolean canBeInlined() {
535        if (isDontInline()) {
536            return false;
537        }
538        return runtime().getCompilerToVM().canInlineMethod(metaspaceMethod);
539    }
540
541    @Override
542    public boolean shouldBeInlined() {
543        if (isForceInline()) {
544            return true;
545        }
546        return runtime().getCompilerToVM().shouldInlineMethod(metaspaceMethod);
547    }
548
549    @Override
550    public LineNumberTable getLineNumberTable() {
551        final boolean hasLineNumberTable = (getConstMethodFlags() & runtime().getConfig().constMethodHasLineNumberTable) != 0;
552        if (!hasLineNumberTable) {
553            return null;
554        }
555
556        long[] values = runtime().getCompilerToVM().getLineNumberTable(metaspaceMethod);
557        if (values.length == 0) {
558            // Empty table so treat is as non-existent
559            return null;
560        }
561        assert values.length % 2 == 0;
562        int[] bci = new int[values.length / 2];
563        int[] line = new int[values.length / 2];
564
565        for (int i = 0; i < values.length / 2; i++) {
566            bci[i] = (int) values[i * 2];
567            line[i] = (int) values[i * 2 + 1];
568        }
569
570        return new LineNumberTableImpl(line, bci);
571    }
572
573    @Override
574    public LocalVariableTable getLocalVariableTable() {
575        final boolean hasLocalVariableTable = (getConstMethodFlags() & runtime().getConfig().constMethodHasLocalVariableTable) != 0;
576        if (!hasLocalVariableTable) {
577            return null;
578        }
579
580        HotSpotVMConfig config = runtime().getConfig();
581        long localVariableTableElement = runtime().getCompilerToVM().getLocalVariableTableStart(metaspaceMethod);
582        final int localVariableTableLength = runtime().getCompilerToVM().getLocalVariableTableLength(metaspaceMethod);
583        Local[] locals = new Local[localVariableTableLength];
584
585        for (int i = 0; i < localVariableTableLength; i++) {
586            final int startBci = unsafe.getChar(localVariableTableElement + config.localVariableTableElementStartBciOffset);
587            final int endBci = startBci + unsafe.getChar(localVariableTableElement + config.localVariableTableElementLengthOffset);
588            final int nameCpIndex = unsafe.getChar(localVariableTableElement + config.localVariableTableElementNameCpIndexOffset);
589            final int typeCpIndex = unsafe.getChar(localVariableTableElement + config.localVariableTableElementDescriptorCpIndexOffset);
590            final int slot = unsafe.getChar(localVariableTableElement + config.localVariableTableElementSlotOffset);
591
592            String localName = getConstantPool().lookupUtf8(nameCpIndex);
593            String localType = getConstantPool().lookupUtf8(typeCpIndex);
594
595            locals[i] = new LocalImpl(localName, runtime().lookupType(localType, holder, false), startBci, endBci, slot);
596
597            // Go to the next LocalVariableTableElement
598            localVariableTableElement += config.localVariableTableElementSize;
599        }
600
601        return new LocalVariableTableImpl(locals);
602    }
603
604    /**
605     * Returns the offset of this method into the v-table. The method must have a v-table entry as
606     * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is
607     * thrown.
608     *
609     * @return the offset of this method into the v-table
610     */
611    public int vtableEntryOffset(ResolvedJavaType resolved) {
612        if (!isInVirtualMethodTable(resolved)) {
613            throw new JVMCIError("%s does not have a vtable entry in type %s", this, resolved);
614        }
615        HotSpotVMConfig config = runtime().getConfig();
616        final int vtableIndex = getVtableIndex((HotSpotResolvedObjectTypeImpl) resolved);
617        return config.instanceKlassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset;
618    }
619
620    @Override
621    public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
622        if (resolved instanceof HotSpotResolvedObjectTypeImpl) {
623            HotSpotResolvedObjectTypeImpl hotspotResolved = (HotSpotResolvedObjectTypeImpl) resolved;
624            int vtableIndex = getVtableIndex(hotspotResolved);
625            return vtableIndex >= 0 && vtableIndex < hotspotResolved.getVtableLength();
626        }
627        return false;
628    }
629
630    private int getVtableIndex(HotSpotResolvedObjectTypeImpl resolved) {
631        if (!holder.isLinked()) {
632            return runtime().getConfig().invalidVtableIndex;
633        }
634        if (holder.isInterface()) {
635            if (resolved.isInterface()) {
636                return runtime().getConfig().invalidVtableIndex;
637            }
638            return getVtableIndexForInterface(resolved);
639        }
640        return getVtableIndex();
641    }
642
643    /**
644     * Returns this method's virtual table index.
645     *
646     * @return virtual table index
647     */
648    private int getVtableIndex() {
649        assert !holder.isInterface();
650        HotSpotVMConfig config = runtime().getConfig();
651        int result = unsafe.getInt(metaspaceMethod + config.methodVtableIndexOffset);
652        assert result >= config.nonvirtualVtableIndex : "must be linked";
653        return result;
654    }
655
656    private int getVtableIndexForInterface(ResolvedJavaType resolved) {
657        HotSpotResolvedObjectTypeImpl hotspotType = (HotSpotResolvedObjectTypeImpl) resolved;
658        return runtime().getCompilerToVM().getVtableIndexForInterface(hotspotType.getMetaspaceKlass(), getMetaspaceMethod());
659    }
660
661    /**
662     * The {@link SpeculationLog} for methods compiled by JVMCI hang off this per-declaring-type
663     * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is
664     * never moves and b) we never read from it.
665     * <p>
666     * One implication is that we will preserve {@link SpeculationLog}s for methods that have been
667     * redefined via class redefinition. It's tempting to periodically flush such logs but we cannot
668     * read the JVM_ACC_IS_OBSOLETE bit (or anything else) via the raw pointer as obsoleted methods
669     * are subject to clean up and deletion (see InstanceKlass::purge_previous_versions_internal).
670     */
671    private static final ClassValue<Map<Long, SpeculationLog>> SpeculationLogs = new ClassValue<Map<Long, SpeculationLog>>() {
672        @Override
673        protected Map<Long, SpeculationLog> computeValue(java.lang.Class<?> type) {
674            return new HashMap<>(4);
675        }
676    };
677
678    public SpeculationLog getSpeculationLog() {
679        Map<Long, SpeculationLog> map = SpeculationLogs.get(holder.mirror());
680        synchronized (map) {
681            SpeculationLog log = map.get(this.metaspaceMethod);
682            if (log == null) {
683                log = new HotSpotSpeculationLog();
684                map.put(metaspaceMethod, log);
685            }
686            return log;
687        }
688    }
689
690    public int intrinsicId() {
691        HotSpotVMConfig config = runtime().getConfig();
692        return unsafe.getByte(metaspaceMethod + config.methodIntrinsicIdOffset) & 0xff;
693    }
694
695    @Override
696    public JavaConstant invoke(JavaConstant receiver, JavaConstant[] arguments) {
697        assert !isConstructor();
698        Method javaMethod = toJava();
699        javaMethod.setAccessible(true);
700
701        Object[] objArguments = new Object[arguments.length];
702        for (int i = 0; i < arguments.length; i++) {
703            objArguments[i] = HotSpotObjectConstantImpl.asBoxedValue(arguments[i]);
704        }
705        Object objReceiver = receiver != null && !receiver.isNull() ? ((HotSpotObjectConstantImpl) receiver).object() : null;
706
707        try {
708            Object objResult = javaMethod.invoke(objReceiver, objArguments);
709            return javaMethod.getReturnType() == void.class ? null : HotSpotObjectConstantImpl.forBoxedValue(getSignature().getReturnKind(), objResult);
710
711        } catch (IllegalAccessException | InvocationTargetException ex) {
712            throw new IllegalArgumentException(ex);
713        }
714    }
715
716    /**
717     * Allocates a compile id for this method by asking the VM for one.
718     *
719     * @param entryBCI entry bci
720     * @return compile id
721     */
722    public int allocateCompileId(int entryBCI) {
723        return runtime().getCompilerToVM().allocateCompileId(metaspaceMethod, entryBCI);
724    }
725
726    public boolean hasCodeAtLevel(int entryBCI, int level) {
727        if (entryBCI == runtime().getConfig().invocationEntryBci) {
728            return hasCompiledCodeAtLevel(level);
729        }
730        return runtime().getCompilerToVM().hasCompiledCodeForOSR(metaspaceMethod, entryBCI, level);
731    }
732
733    private int methodId;
734
735    public void setMethodId(int id) {
736        assert methodId == 0;
737        methodId = id;
738    }
739
740    public int getMethodId() {
741        return methodId;
742    }
743}