1 /*
2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 package jdk.vm.ci.hotspot;
24 
25 import static jdk.vm.ci.hotspot.CompilerToVM.compilerToVM;
26 import static jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.runtime;
27 import static jdk.vm.ci.hotspot.HotSpotModifiers.BRIDGE;
28 import static jdk.vm.ci.hotspot.HotSpotModifiers.SYNTHETIC;
29 import static jdk.vm.ci.hotspot.HotSpotModifiers.VARARGS;
30 import static jdk.vm.ci.hotspot.HotSpotModifiers.jvmMethodModifiers;
31 import static jdk.vm.ci.hotspot.HotSpotVMConfig.config;
32 import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
33 
34 import java.lang.annotation.Annotation;
35 import java.lang.reflect.Executable;
36 import java.lang.reflect.Modifier;
37 import java.lang.reflect.Type;
38 import java.util.HashMap;
39 import java.util.Map;
40 
41 import jdk.vm.ci.common.JVMCIError;
42 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime.Option;
43 import jdk.vm.ci.meta.Constant;
44 import jdk.vm.ci.meta.ConstantPool;
45 import jdk.vm.ci.meta.DefaultProfilingInfo;
46 import jdk.vm.ci.meta.ExceptionHandler;
47 import jdk.vm.ci.meta.JavaMethod;
48 import jdk.vm.ci.meta.JavaType;
49 import jdk.vm.ci.meta.LineNumberTable;
50 import jdk.vm.ci.meta.Local;
51 import jdk.vm.ci.meta.LocalVariableTable;
52 import jdk.vm.ci.meta.ProfilingInfo;
53 import jdk.vm.ci.meta.ResolvedJavaMethod;
54 import jdk.vm.ci.meta.ResolvedJavaType;
55 import jdk.vm.ci.meta.SpeculationLog;
56 import jdk.vm.ci.meta.TriState;
57 
58 /**
59  * Implementation of {@link JavaMethod} for resolved HotSpot methods.
60  */
61 final class HotSpotResolvedJavaMethodImpl extends HotSpotMethod implements HotSpotResolvedJavaMethod, MetaspaceWrapperObject {
62 
63     /**
64      * Reference to metaspace Method object.
65      */
66     private final long metaspaceMethod;
67 
68     private final HotSpotResolvedObjectTypeImpl holder;
69     private final HotSpotConstantPool constantPool;
70     private final HotSpotSignature signature;
71     private HotSpotMethodData methodData;
72     private byte[] code;
73 
74     /**
75      * Cache for {@link #toJava()}.
76      */
77     private volatile Executable toJavaCache;
78 
79     /**
80      * Only 30% of {@link HotSpotResolvedJavaMethodImpl}s have their name accessed so compute it
81      * lazily and cache it.
82      */
83     private String nameCache;
84 
85     /**
86      * Gets the holder of a HotSpot metaspace method native object.
87      *
88      * @param metaspaceMethod a metaspace Method object
89      * @return the {@link ResolvedJavaType} corresponding to the holder of the
90      *         {@code metaspaceMethod}
91      */
getHolder(long metaspaceMethod)92     private static HotSpotResolvedObjectTypeImpl getHolder(long metaspaceMethod) {
93         HotSpotVMConfig config = config();
94         final long metaspaceConstMethod = UNSAFE.getAddress(metaspaceMethod + config.methodConstMethodOffset);
95         final long metaspaceConstantPool = UNSAFE.getAddress(metaspaceConstMethod + config.constMethodConstantsOffset);
96         return compilerToVM().getResolvedJavaType(null, metaspaceConstantPool + config.constantPoolHolderOffset, false);
97     }
98 
99     /**
100      * Gets the JVMCI mirror from a HotSpot method. The VM is responsible for ensuring that the
101      * Method* is kept alive for the duration of this call and the
102      * {@link HotSpotJVMCIMetaAccessContext} keeps it alive after that.
103      *
104      * Called from the VM.
105      *
106      * @param metaspaceMethod a metaspace Method object
107      * @return the {@link ResolvedJavaMethod} corresponding to {@code metaspaceMethod}
108      */
109     @SuppressWarnings("unused")
fromMetaspace(long metaspaceMethod)110     private static HotSpotResolvedJavaMethod fromMetaspace(long metaspaceMethod) {
111         HotSpotResolvedObjectTypeImpl holder = getHolder(metaspaceMethod);
112         return holder.createMethod(metaspaceMethod);
113     }
114 
HotSpotResolvedJavaMethodImpl(HotSpotResolvedObjectTypeImpl holder, long metaspaceMethod)115     HotSpotResolvedJavaMethodImpl(HotSpotResolvedObjectTypeImpl holder, long metaspaceMethod) {
116         this.metaspaceMethod = metaspaceMethod;
117         this.holder = holder;
118 
119         HotSpotVMConfig config = config();
120         final long constMethod = getConstMethod();
121 
122         /*
123          * Get the constant pool from the metaspace method. Some methods (e.g. intrinsics for
124          * signature-polymorphic method handle methods) have their own constant pool instead of the
125          * one from their holder.
126          */
127         final long metaspaceConstantPool = UNSAFE.getAddress(constMethod + config.constMethodConstantsOffset);
128         if (metaspaceConstantPool == holder.getConstantPool().getMetaspaceConstantPool()) {
129             this.constantPool = holder.getConstantPool();
130         } else {
131             this.constantPool = compilerToVM().getConstantPool(this);
132         }
133 
134         final int signatureIndex = UNSAFE.getChar(constMethod + config.constMethodSignatureIndexOffset);
135         this.signature = (HotSpotSignature) constantPool.lookupSignature(signatureIndex);
136     }
137 
138     /**
139      * Returns a pointer to this method's constant method data structure (
140      * {@code Method::_constMethod}). This pointer isn't wrapped since it should be safe to use it
141      * within the context of this HotSpotResolvedJavaMethodImpl since the Method* and ConstMethod*
142      * are kept alive as a pair.
143      *
144      * @return pointer to this method's ConstMethod
145      */
getConstMethod()146     private long getConstMethod() {
147         assert metaspaceMethod != 0;
148         return UNSAFE.getAddress(metaspaceMethod + config().methodConstMethodOffset);
149     }
150 
151     @Override
getName()152     public String getName() {
153         if (nameCache == null) {
154             final int nameIndex = UNSAFE.getChar(getConstMethod() + config().constMethodNameIndexOffset);
155             nameCache = constantPool.lookupUtf8(nameIndex);
156         }
157         return nameCache;
158     }
159 
160     @Override
equals(Object obj)161     public boolean equals(Object obj) {
162         if (this == obj) {
163             return true;
164         }
165         if (obj instanceof HotSpotResolvedJavaMethodImpl) {
166             HotSpotResolvedJavaMethodImpl that = (HotSpotResolvedJavaMethodImpl) obj;
167             return that.metaspaceMethod == metaspaceMethod;
168         }
169         return false;
170     }
171 
172     @Override
hashCode()173     public int hashCode() {
174         return (int) metaspaceMethod;
175     }
176 
177     /**
178      * Returns this method's flags ({@code Method::_flags}).
179      *
180      * @return flags of this method
181      */
getFlags()182     private int getFlags() {
183         return UNSAFE.getShort(metaspaceMethod + config().methodFlagsOffset);
184     }
185 
186     /**
187      * Returns this method's constant method flags ({@code ConstMethod::_flags}).
188      *
189      * @return flags of this method's ConstMethod
190      */
getConstMethodFlags()191     private int getConstMethodFlags() {
192         return UNSAFE.getChar(getConstMethod() + config().constMethodFlagsOffset);
193     }
194 
195     @Override
getDeclaringClass()196     public HotSpotResolvedObjectTypeImpl getDeclaringClass() {
197         return holder;
198     }
199 
200     /**
201      * Gets the address of the C++ Method object for this method.
202      */
getMetaspaceMethodConstant()203     public Constant getMetaspaceMethodConstant() {
204         return HotSpotMetaspaceConstantImpl.forMetaspaceObject(this, false);
205     }
206 
207     @Override
getMetaspacePointer()208     public long getMetaspacePointer() {
209         return metaspaceMethod;
210     }
211 
212     @Override
getEncoding()213     public Constant getEncoding() {
214         return getMetaspaceMethodConstant();
215     }
216 
217     /**
218      * Gets the complete set of modifiers for this method which includes the JVM specification
219      * modifiers as well as the HotSpot internal modifiers.
220      */
getAllModifiers()221     public int getAllModifiers() {
222         return UNSAFE.getInt(metaspaceMethod + config().methodAccessFlagsOffset);
223     }
224 
225     @Override
getModifiers()226     public int getModifiers() {
227         return getAllModifiers() & jvmMethodModifiers();
228     }
229 
230     @Override
canBeStaticallyBound()231     public boolean canBeStaticallyBound() {
232         return (isFinal() || isPrivate() || isStatic() || holder.isLeaf()) && isConcrete();
233     }
234 
235     @Override
getCode()236     public byte[] getCode() {
237         if (getCodeSize() == 0) {
238             return null;
239         }
240         if (code == null && holder.isLinked()) {
241             code = compilerToVM().getBytecode(this);
242             assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length;
243         }
244         return code;
245     }
246 
247     @Override
getCodeSize()248     public int getCodeSize() {
249         return UNSAFE.getChar(getConstMethod() + config().constMethodCodeSizeOffset);
250     }
251 
252     @Override
getExceptionHandlers()253     public ExceptionHandler[] getExceptionHandlers() {
254         final boolean hasExceptionTable = (getConstMethodFlags() & config().constMethodHasExceptionTable) != 0;
255         if (!hasExceptionTable) {
256             return new ExceptionHandler[0];
257         }
258 
259         HotSpotVMConfig config = config();
260         final int exceptionTableLength = compilerToVM().getExceptionTableLength(this);
261         ExceptionHandler[] handlers = new ExceptionHandler[exceptionTableLength];
262         long exceptionTableElement = compilerToVM().getExceptionTableStart(this);
263 
264         for (int i = 0; i < exceptionTableLength; i++) {
265             final int startPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementStartPcOffset);
266             final int endPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementEndPcOffset);
267             final int handlerPc = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementHandlerPcOffset);
268             int catchTypeIndex = UNSAFE.getChar(exceptionTableElement + config.exceptionTableElementCatchTypeIndexOffset);
269 
270             JavaType catchType;
271             if (catchTypeIndex == 0) {
272                 catchType = null;
273             } else {
274                 final int opcode = -1;  // opcode is not used
275                 catchType = constantPool.lookupType(catchTypeIndex, opcode);
276 
277                 // Check for Throwable which catches everything.
278                 if (catchType instanceof HotSpotResolvedObjectTypeImpl) {
279                     HotSpotResolvedObjectTypeImpl resolvedType = (HotSpotResolvedObjectTypeImpl) catchType;
280                     if (resolvedType.mirror() == Throwable.class) {
281                         catchTypeIndex = 0;
282                         catchType = null;
283                     }
284                 }
285             }
286             handlers[i] = new ExceptionHandler(startPc, endPc, handlerPc, catchTypeIndex, catchType);
287 
288             // Go to the next ExceptionTableElement
289             exceptionTableElement += config.exceptionTableElementSize;
290         }
291 
292         return handlers;
293     }
294 
295     /**
296      * Returns true if this method has a {@code CallerSensitive} annotation.
297      *
298      * @return true if CallerSensitive annotation present, false otherwise
299      */
300     @Override
isCallerSensitive()301     public boolean isCallerSensitive() {
302         return (getFlags() & config().methodFlagsCallerSensitive) != 0;
303     }
304 
305     /**
306      * Returns true if this method has a {@code ForceInline} annotation.
307      *
308      * @return true if ForceInline annotation present, false otherwise
309      */
310     @Override
isForceInline()311     public boolean isForceInline() {
312         return (getFlags() & config().methodFlagsForceInline) != 0;
313     }
314 
315     /**
316      * Returns true if this method has a {@code ReservedStackAccess} annotation.
317      *
318      * @return true if ReservedStackAccess annotation present, false otherwise
319      */
320     @Override
hasReservedStackAccess()321     public boolean hasReservedStackAccess() {
322         return (getFlags() & config().methodFlagsReservedStackAccess) != 0;
323     }
324 
325     /**
326      * Sets flags on {@code method} indicating that it should never be inlined or compiled by the
327      * VM.
328      */
329     @Override
setNotInlinableOrCompilable()330     public void setNotInlinableOrCompilable() {
331         compilerToVM().setNotInlinableOrCompilable(this);
332     }
333 
334     /**
335      * Returns true if this method is one of the special methods that is ignored by security stack
336      * walks.
337      *
338      * @return true if special method ignored by security stack walks, false otherwise
339      */
340     @Override
ignoredBySecurityStackWalk()341     public boolean ignoredBySecurityStackWalk() {
342         return compilerToVM().methodIsIgnoredBySecurityStackWalk(this);
343     }
344 
345     @Override
isClassInitializer()346     public boolean isClassInitializer() {
347         if (isStatic()) {
348             final int nameIndex = UNSAFE.getChar(getConstMethod() + config().constMethodNameIndexOffset);
349             long nameSymbol = constantPool.getEntryAt(nameIndex);
350             long clinitSymbol = config().symbolClinit;
351             return nameSymbol == clinitSymbol;
352         }
353         return false;
354     }
355 
356     @Override
isConstructor()357     public boolean isConstructor() {
358         if (!isStatic()) {
359             final int nameIndex = UNSAFE.getChar(getConstMethod() + config().constMethodNameIndexOffset);
360             long nameSymbol = constantPool.getEntryAt(nameIndex);
361             long initSymbol = config().symbolInit;
362             return nameSymbol == initSymbol;
363         }
364         return false;
365     }
366 
367     @Override
getMaxLocals()368     public int getMaxLocals() {
369         if (isAbstract() || isNative()) {
370             return 0;
371         }
372         HotSpotVMConfig config = config();
373         return UNSAFE.getChar(getConstMethod() + config.methodMaxLocalsOffset);
374     }
375 
376     @Override
getMaxStackSize()377     public int getMaxStackSize() {
378         if (isAbstract() || isNative()) {
379             return 0;
380         }
381         HotSpotVMConfig config = config();
382         return config.extraStackEntries + UNSAFE.getChar(getConstMethod() + config.constMethodMaxStackOffset);
383     }
384 
385     @Override
asStackTraceElement(int bci)386     public StackTraceElement asStackTraceElement(int bci) {
387         if (bci < 0 || bci >= getCodeSize()) {
388             // HotSpot code can only construct stack trace elements for valid bcis
389             StackTraceElement ste = compilerToVM().getStackTraceElement(this, 0);
390             return new StackTraceElement(ste.getClassName(), ste.getMethodName(), ste.getFileName(), -1);
391         }
392         return compilerToVM().getStackTraceElement(this, bci);
393     }
394 
395     @Override
uniqueConcreteMethod(HotSpotResolvedObjectType receiver)396     public ResolvedJavaMethod uniqueConcreteMethod(HotSpotResolvedObjectType receiver) {
397         if (receiver.isInterface()) {
398             // Cannot trust interfaces. Because of:
399             // interface I { void foo(); }
400             // class A { public void foo() {} }
401             // class B extends A implements I { }
402             // class C extends B { public void foo() { } }
403             // class D extends B { }
404             // Would lead to identify C.foo() as the unique concrete method for I.foo() without
405             // seeing A.foo().
406             return null;
407         }
408         if (this.isDefault()) {
409             // CHA for default methods doesn't work and may crash the VM
410             return null;
411         }
412         return compilerToVM().findUniqueConcreteMethod(((HotSpotResolvedObjectTypeImpl) receiver), this);
413     }
414 
415     @Override
getSignature()416     public HotSpotSignature getSignature() {
417         return signature;
418     }
419 
420     /**
421      * Gets the value of {@code Method::_code}.
422      *
423      * @return the value of {@code Method::_code}
424      */
getCompiledCode()425     private long getCompiledCode() {
426         HotSpotVMConfig config = config();
427         return UNSAFE.getAddress(metaspaceMethod + config.methodCodeOffset);
428     }
429 
430     /**
431      * Returns whether this method has compiled code.
432      *
433      * @return true if this method has compiled code, false otherwise
434      */
435     @Override
hasCompiledCode()436     public boolean hasCompiledCode() {
437         return getCompiledCode() != 0L;
438     }
439 
440     /**
441      * @param level
442      * @return true if the currently installed code was generated at {@code level}.
443      */
444     @Override
hasCompiledCodeAtLevel(int level)445     public boolean hasCompiledCodeAtLevel(int level) {
446         long compiledCode = getCompiledCode();
447         if (compiledCode != 0) {
448             return UNSAFE.getInt(compiledCode + config().nmethodCompLevelOffset) == level;
449         }
450         return false;
451     }
452 
453     @Override
getProfilingInfo(boolean includeNormal, boolean includeOSR)454     public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) {
455         ProfilingInfo info;
456 
457         if (Option.UseProfilingInformation.getBoolean() && methodData == null) {
458             long metaspaceMethodData = UNSAFE.getAddress(metaspaceMethod + config().methodDataOffset);
459             if (metaspaceMethodData != 0) {
460                 methodData = new HotSpotMethodData(metaspaceMethodData, this);
461                 String methodDataFilter = Option.TraceMethodDataFilter.getString();
462                 if (methodDataFilter != null && this.format("%H.%n").contains(methodDataFilter)) {
463                     System.out.println(methodData.toString());
464                 }
465             }
466         }
467 
468         if (methodData == null || (!methodData.hasNormalData() && !methodData.hasExtraData())) {
469             // Be optimistic and return false for exceptionSeen. A methodDataOop is allocated in
470             // case of a deoptimization.
471             info = DefaultProfilingInfo.get(TriState.FALSE);
472         } else {
473             info = new HotSpotProfilingInfo(methodData, this, includeNormal, includeOSR);
474         }
475         return info;
476     }
477 
478     @Override
reprofile()479     public void reprofile() {
480         compilerToVM().reprofile(this);
481     }
482 
483     @Override
getConstantPool()484     public ConstantPool getConstantPool() {
485         return constantPool;
486     }
487 
488     @Override
getParameters()489     public Parameter[] getParameters() {
490         if (signature.getParameterCount(false) == 0) {
491             return new ResolvedJavaMethod.Parameter[0];
492         }
493         java.lang.reflect.Parameter[] javaParameters = toJava().getParameters();
494         Parameter[] res = new Parameter[javaParameters.length];
495         for (int i = 0; i < res.length; i++) {
496             java.lang.reflect.Parameter src = javaParameters[i];
497             String paramName = src.isNamePresent() ? src.getName() : null;
498             res[i] = new Parameter(paramName, src.getModifiers(), this, i);
499         }
500         return res;
501     }
502 
503     @Override
getParameterAnnotations()504     public Annotation[][] getParameterAnnotations() {
505         if ((getConstMethodFlags() & config().constMethodHasParameterAnnotations) == 0) {
506             return new Annotation[signature.getParameterCount(false)][0];
507         }
508         return toJava().getParameterAnnotations();
509     }
510 
511     @Override
getAnnotations()512     public Annotation[] getAnnotations() {
513         if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0) {
514             return new Annotation[0];
515         }
516         return toJava().getAnnotations();
517     }
518 
519     @Override
getDeclaredAnnotations()520     public Annotation[] getDeclaredAnnotations() {
521         if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0) {
522             return new Annotation[0];
523         }
524         return toJava().getDeclaredAnnotations();
525     }
526 
527     @Override
getAnnotation(Class<T> annotationClass)528     public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
529         if ((getConstMethodFlags() & config().constMethodHasMethodAnnotations) == 0) {
530             return null;
531         }
532         return toJava().getAnnotation(annotationClass);
533     }
534 
535     @Override
isBridge()536     public boolean isBridge() {
537         return (BRIDGE & getModifiers()) != 0;
538     }
539 
540     @Override
isSynthetic()541     public boolean isSynthetic() {
542         return (SYNTHETIC & getModifiers()) != 0;
543     }
544 
545     @Override
isVarArgs()546     public boolean isVarArgs() {
547         return (VARARGS & getModifiers()) != 0;
548     }
549 
550     @Override
isDefault()551     public boolean isDefault() {
552         // Copied from java.lang.Method.isDefault()
553         int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
554         return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
555     }
556 
557     @Override
getGenericParameterTypes()558     public Type[] getGenericParameterTypes() {
559         if (isClassInitializer()) {
560             return new Type[0];
561         }
562         return toJava().getGenericParameterTypes();
563     }
564 
toJava()565     private Executable toJava() {
566         if (toJavaCache == null) {
567             assert !isClassInitializer() : this;
568             synchronized (this) {
569                 if (toJavaCache == null) {
570                     toJavaCache = compilerToVM().asReflectionExecutable(this);
571                 }
572             }
573         }
574         return toJavaCache;
575     }
576 
577     @Override
canBeInlined()578     public boolean canBeInlined() {
579         if (hasNeverInlineDirective()) {
580             return false;
581         }
582         return compilerToVM().isCompilable(this);
583     }
584 
585     @Override
hasNeverInlineDirective()586     public boolean hasNeverInlineDirective() {
587         return compilerToVM().hasNeverInlineDirective(this);
588     }
589 
590     @Override
shouldBeInlined()591     public boolean shouldBeInlined() {
592         if (isForceInline()) {
593             return true;
594         }
595         return compilerToVM().shouldInlineMethod(this);
596     }
597 
598     @Override
getLineNumberTable()599     public LineNumberTable getLineNumberTable() {
600         final boolean hasLineNumberTable = (getConstMethodFlags() & config().constMethodHasLineNumberTable) != 0;
601         if (!hasLineNumberTable) {
602             return null;
603         }
604 
605         long[] values = compilerToVM().getLineNumberTable(this);
606         if (values == null || values.length == 0) {
607             // Empty table so treat is as non-existent
608             return null;
609         }
610         assert values.length % 2 == 0;
611         int[] bci = new int[values.length / 2];
612         int[] line = new int[values.length / 2];
613 
614         for (int i = 0; i < values.length / 2; i++) {
615             bci[i] = (int) values[i * 2];
616             line[i] = (int) values[i * 2 + 1];
617         }
618 
619         return new LineNumberTable(line, bci);
620     }
621 
622     @Override
getLocalVariableTable()623     public LocalVariableTable getLocalVariableTable() {
624         final boolean hasLocalVariableTable = (getConstMethodFlags() & config().constMethodHasLocalVariableTable) != 0;
625         if (!hasLocalVariableTable) {
626             return null;
627         }
628 
629         HotSpotVMConfig config = config();
630         long localVariableTableElement = compilerToVM().getLocalVariableTableStart(this);
631         final int localVariableTableLength = compilerToVM().getLocalVariableTableLength(this);
632         Local[] locals = new Local[localVariableTableLength];
633 
634         for (int i = 0; i < localVariableTableLength; i++) {
635             final int startBci = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementStartBciOffset);
636             final int endBci = startBci + UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementLengthOffset);
637             final int nameCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementNameCpIndexOffset);
638             final int typeCpIndex = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementDescriptorCpIndexOffset);
639             final int slot = UNSAFE.getChar(localVariableTableElement + config.localVariableTableElementSlotOffset);
640 
641             String localName = getConstantPool().lookupUtf8(nameCpIndex);
642             String localType = getConstantPool().lookupUtf8(typeCpIndex);
643 
644             locals[i] = new Local(localName, runtime().lookupType(localType, holder, false), startBci, endBci, slot);
645 
646             // Go to the next LocalVariableTableElement
647             localVariableTableElement += config.localVariableTableElementSize;
648         }
649 
650         return new LocalVariableTable(locals);
651     }
652 
653     /**
654      * Returns the offset of this method into the v-table. The method must have a v-table entry as
655      * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is
656      * thrown.
657      *
658      * @return the offset of this method into the v-table
659      */
660     @Override
vtableEntryOffset(ResolvedJavaType resolved)661     public int vtableEntryOffset(ResolvedJavaType resolved) {
662         if (!isInVirtualMethodTable(resolved)) {
663             throw new JVMCIError("%s does not have a vtable entry in type %s", this, resolved);
664         }
665         HotSpotVMConfig config = config();
666         final int vtableIndex = getVtableIndex((HotSpotResolvedObjectTypeImpl) resolved);
667         return config.klassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset;
668     }
669 
670     @Override
isInVirtualMethodTable(ResolvedJavaType resolved)671     public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
672         if (resolved instanceof HotSpotResolvedObjectTypeImpl) {
673             HotSpotResolvedObjectTypeImpl hotspotResolved = (HotSpotResolvedObjectTypeImpl) resolved;
674             int vtableIndex = getVtableIndex(hotspotResolved);
675             return vtableIndex >= 0 && vtableIndex < hotspotResolved.getVtableLength();
676         }
677         return false;
678     }
679 
getVtableIndex(HotSpotResolvedObjectTypeImpl resolved)680     private int getVtableIndex(HotSpotResolvedObjectTypeImpl resolved) {
681         if (!holder.isLinked()) {
682             return config().invalidVtableIndex;
683         }
684         if (holder.isInterface()) {
685             if (resolved.isInterface() || !resolved.isLinked()) {
686                 return config().invalidVtableIndex;
687             }
688             return getVtableIndexForInterfaceMethod(resolved);
689         }
690         return getVtableIndex();
691     }
692 
693     /**
694      * Returns this method's virtual table index.
695      *
696      * @return virtual table index
697      */
getVtableIndex()698     private int getVtableIndex() {
699         assert !holder.isInterface();
700         HotSpotVMConfig config = config();
701         int result = UNSAFE.getInt(metaspaceMethod + config.methodVtableIndexOffset);
702         assert result >= config.nonvirtualVtableIndex : "must be linked";
703         return result;
704     }
705 
getVtableIndexForInterfaceMethod(ResolvedJavaType resolved)706     private int getVtableIndexForInterfaceMethod(ResolvedJavaType resolved) {
707         HotSpotResolvedObjectTypeImpl hotspotType = (HotSpotResolvedObjectTypeImpl) resolved;
708         return compilerToVM().getVtableIndexForInterfaceMethod(hotspotType, this);
709     }
710 
711     /**
712      * The {@link SpeculationLog} for methods compiled by JVMCI hang off this per-declaring-type
713      * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is
714      * never moves and b) we never read from it.
715      * <p>
716      * One implication is that we will preserve {@link SpeculationLog}s for methods that have been
717      * redefined via class redefinition. It's tempting to periodically flush such logs but we cannot
718      * read the JVM_ACC_IS_OBSOLETE bit (or anything else) via the raw pointer as obsoleted methods
719      * are subject to clean up and deletion (see InstanceKlass::purge_previous_versions_internal).
720      */
721     private static final ClassValue<Map<Long, SpeculationLog>> SpeculationLogs = new ClassValue<>() {
722         @Override
723         protected Map<Long, SpeculationLog> computeValue(java.lang.Class<?> type) {
724             return new HashMap<>(4);
725         }
726     };
727 
728     @Override
getSpeculationLog()729     public SpeculationLog getSpeculationLog() {
730         Map<Long, SpeculationLog> map = SpeculationLogs.get(holder.mirror());
731         synchronized (map) {
732             SpeculationLog log = map.get(this.metaspaceMethod);
733             if (log == null) {
734                 log = new HotSpotSpeculationLog();
735                 map.put(metaspaceMethod, log);
736             }
737             return log;
738         }
739     }
740 
741     @Override
intrinsicId()742     public int intrinsicId() {
743         HotSpotVMConfig config = config();
744         return UNSAFE.getChar(metaspaceMethod + config.methodIntrinsicIdOffset);
745     }
746 
747     @Override
isIntrinsicCandidate()748     public boolean isIntrinsicCandidate() {
749         return (getFlags() & config().methodFlagsIntrinsicCandidate) != 0;
750     }
751 
752     /**
753      * Allocates a compile id for this method by asking the VM for one.
754      *
755      * @param entryBCI entry bci
756      * @return compile id
757      */
758     @Override
allocateCompileId(int entryBCI)759     public int allocateCompileId(int entryBCI) {
760         return compilerToVM().allocateCompileId(this, entryBCI);
761     }
762 
763     @Override
hasCodeAtLevel(int entryBCI, int level)764     public boolean hasCodeAtLevel(int entryBCI, int level) {
765         if (entryBCI == config().invocationEntryBci) {
766             return hasCompiledCodeAtLevel(level);
767         }
768         return compilerToVM().hasCompiledCodeForOSR(this, entryBCI, level);
769     }
770 
771     @Override
methodIdnum()772     public int methodIdnum() {
773         return UNSAFE.getChar(getConstMethod() + config().constMethodMethodIdnumOffset);
774     }
775 }
776