001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.runtime;
014    
015    import org.jikesrvm.ArchitectureSpecific.CodeArray;
016    import org.jikesrvm.ArchitectureSpecific.DynamicLinkerHelper;
017    import org.jikesrvm.VM;
018    import org.jikesrvm.Constants;
019    import org.jikesrvm.classloader.RVMClass;
020    import org.jikesrvm.classloader.RVMMethod;
021    import org.jikesrvm.classloader.MethodReference;
022    import org.jikesrvm.compilers.common.CompiledMethod;
023    import org.jikesrvm.compilers.common.CompiledMethods;
024    import org.vmmagic.pragma.DynamicBridge;
025    import org.vmmagic.pragma.Entrypoint;
026    import org.vmmagic.pragma.NoInline;
027    import org.vmmagic.unboxed.Address;
028    import org.vmmagic.unboxed.Offset;
029    
030    /**
031     * Implement lazy compilation.
032     */
033    @DynamicBridge
034    public class DynamicLinker implements Constants {
035    
036      /**
037       * Resolve, compile if necessary, and invoke a method.
038       * <pre>
039       *  Taken:    nothing (calling context is implicit)
040       *  Returned: does not return (method dispatch table is updated and method is executed)
041       * </pre>
042       */
043      @Entrypoint
044      static void lazyMethodInvoker() {
045        DynamicLink dl = DL_Helper.resolveDynamicInvocation();
046        RVMMethod targMethod = DL_Helper.resolveMethodRef(dl);
047        DL_Helper.compileMethod(dl, targMethod);
048        CodeArray code = targMethod.getCurrentEntryCodeArray();
049        Magic.dynamicBridgeTo(code);                   // restore parameters and invoke
050        if (VM.VerifyAssertions) VM._assert(NOT_REACHED);  // does not return here
051      }
052    
053      /**
054       * Report unimplemented native method error.
055       * <pre>
056       *  Taken:    nothing (calling context is implicit)
057       *  Returned: does not return (throws UnsatisfiedLinkError)
058       * </pre>
059       */
060      @Entrypoint
061      static void unimplementedNativeMethod() {
062        DynamicLink dl = DL_Helper.resolveDynamicInvocation();
063        RVMMethod targMethod = DL_Helper.resolveMethodRef(dl);
064        throw new UnsatisfiedLinkError(targMethod.toString());
065      }
066    
067      /**
068       * Report a magic SysCall has been mistakenly invoked
069       */
070      @Entrypoint
071      static void sysCallMethod() {
072        DynamicLink dl = DL_Helper.resolveDynamicInvocation();
073        RVMMethod targMethod = DL_Helper.resolveMethodRef(dl);
074        throw new UnsatisfiedLinkError(targMethod.toString() + " which is a SysCall");
075      }
076    
077      /**
078       * Helper class that does the real work of resolving method references
079       * and compiling a lazy method invocation.  In separate class so
080       * that it doesn't implement DynamicBridge magic.
081       */
082      private static class DL_Helper {
083    
084        /**
085         * Discover method reference to be invoked via dynamic bridge.
086         * <pre>
087         * Taken:       nothing (call stack is examined to find invocation site)
088         * Returned:    DynamicLink that describes call site.
089         * </pre>
090         */
091        @NoInline
092        static DynamicLink resolveDynamicInvocation() {
093    
094          // find call site
095          //
096          VM.disableGC();
097          Address callingFrame = Magic.getCallerFramePointer(Magic.getFramePointer());
098          Address returnAddress = Magic.getReturnAddressUnchecked(callingFrame);
099          callingFrame = Magic.getCallerFramePointer(callingFrame);
100          int callingCompiledMethodId = Magic.getCompiledMethodID(callingFrame);
101          CompiledMethod callingCompiledMethod = CompiledMethods.getCompiledMethod(callingCompiledMethodId);
102          Offset callingInstructionOffset = callingCompiledMethod.getInstructionOffset(returnAddress);
103          VM.enableGC();
104    
105          // obtain symbolic method reference
106          //
107          DynamicLink dynamicLink = new DynamicLink();
108          callingCompiledMethod.getDynamicLink(dynamicLink, callingInstructionOffset);
109    
110          return dynamicLink;
111        }
112    
113        /**
114         * Resolve method ref into appropriate RVMMethod
115         * <pre>
116         * Taken:       DynamicLink that describes call site.
117         * Returned:    RVMMethod that should be invoked.
118         * </pre>
119         */
120        @NoInline
121        static RVMMethod resolveMethodRef(DynamicLink dynamicLink) {
122          // resolve symbolic method reference into actual method
123          //
124          MethodReference methodRef = dynamicLink.methodRef();
125          if (dynamicLink.isInvokeSpecial()) {
126            return methodRef.resolveInvokeSpecial();
127          } else if (dynamicLink.isInvokeStatic()) {
128            return methodRef.resolve();
129          } else {
130            // invokevirtual or invokeinterface
131            VM.disableGC();
132            Object targetObject = DynamicLinkerHelper.getReceiverObject();
133            VM.enableGC();
134            RVMClass targetClass = Magic.getObjectType(targetObject).asClass();
135            RVMMethod targetMethod = targetClass.findVirtualMethod(methodRef.getName(), methodRef.getDescriptor());
136            if (targetMethod == null) {
137              throw new IncompatibleClassChangeError(targetClass.getDescriptor().classNameFromDescriptor());
138            }
139            return targetMethod;
140          }
141        }
142    
143        /**
144         * Compile (if necessary) targetMethod and patch the appropriate disaptch tables
145         * @param targetMethod the RVMMethod to compile (if not already compiled)
146         */
147        @NoInline
148        static void compileMethod(DynamicLink dynamicLink, RVMMethod targetMethod) {
149    
150          RVMClass targetClass = targetMethod.getDeclaringClass();
151    
152          // if necessary, compile method
153          //
154          if (!targetMethod.isCompiled()) {
155            targetMethod.compile();
156    
157            // If targetMethod is a virtual method, then eagerly patch tib of declaring class.
158            // (we need to do this to get the method test used by opt to work with lazy compilation).
159            if (!(targetMethod.isObjectInitializer() || targetMethod.isStatic())) {
160              targetClass.updateTIBEntry(targetMethod);
161            }
162          }
163    
164          // patch appropriate dispatch table
165          //
166          if (targetMethod.isObjectInitializer() || targetMethod.isStatic()) {
167            targetClass.updateJTOCEntry(targetMethod);
168          } else if (dynamicLink.isInvokeSpecial()) {
169            targetClass.updateTIBEntry(targetMethod);
170          } else {
171            VM.disableGC();
172            Object targetObject = DynamicLinkerHelper.getReceiverObject();
173            VM.enableGC();
174            RVMClass recvClass = (RVMClass) Magic.getObjectType(targetObject);
175            recvClass.updateTIBEntry(targetMethod);
176          }
177        }
178      }
179    }