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