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 */
013package org.jikesrvm.runtime;
014
015import static org.jikesrvm.VM.NOT_REACHED;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.classloader.RVMClass;
019import org.jikesrvm.classloader.RVMMethod;
020import org.jikesrvm.classloader.MethodReference;
021import org.jikesrvm.compilers.common.CodeArray;
022import org.jikesrvm.compilers.common.CompiledMethod;
023import org.jikesrvm.compilers.common.CompiledMethods;
024import org.vmmagic.pragma.DynamicBridge;
025import org.vmmagic.pragma.Entrypoint;
026import org.vmmagic.pragma.NoInline;
027import org.vmmagic.unboxed.Address;
028import org.vmmagic.unboxed.Offset;
029
030/**
031 * Implement lazy compilation.
032 */
033@DynamicBridge
034public class DynamicLinker {
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     * Discovers a method reference to be invoked via dynamic bridge. The call
086     * stack is examined to find the invocation site.
087     *
088     * @return an DynamicLink describing the call site
089     */
090    @NoInline
091    static DynamicLink resolveDynamicInvocation() {
092
093      // find call site
094      //
095      VM.disableGC();
096      Address callingFrame = Magic.getCallerFramePointer(Magic.getFramePointer());
097      Address returnAddress = Magic.getReturnAddressUnchecked(callingFrame);
098      callingFrame = Magic.getCallerFramePointer(callingFrame);
099      int callingCompiledMethodId = Magic.getCompiledMethodID(callingFrame);
100      CompiledMethod callingCompiledMethod = CompiledMethods.getCompiledMethod(callingCompiledMethodId);
101      Offset callingInstructionOffset = callingCompiledMethod.getInstructionOffset(returnAddress);
102      VM.enableGC();
103
104      // obtain symbolic method reference
105      //
106      DynamicLink dynamicLink = new DynamicLink();
107      callingCompiledMethod.getDynamicLink(dynamicLink, callingInstructionOffset);
108
109      return dynamicLink;
110    }
111
112    /**
113     * Resolves a method ref into appropriate RVMMethod.
114     *
115     * @param dynamicLink a DynamicLink that describes call site
116     * @return the RVMMethod that should be invoked.
117     */
118    @NoInline
119    static RVMMethod resolveMethodRef(DynamicLink dynamicLink) {
120      // resolve symbolic method reference into actual method
121      //
122      MethodReference methodRef = dynamicLink.methodRef();
123      if (dynamicLink.isInvokeSpecial()) {
124        return methodRef.resolveInvokeSpecial();
125      } else if (dynamicLink.isInvokeStatic()) {
126        return methodRef.resolve();
127      } else {
128        // invokevirtual or invokeinterface
129        VM.disableGC();
130        Object targetObject;
131        if (VM.BuildForIA32) {
132          targetObject = org.jikesrvm.ia32.DynamicLinkerHelper.getReceiverObject();
133        } else {
134          if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
135          targetObject = org.jikesrvm.ppc.DynamicLinkerHelper.getReceiverObject();
136        }
137        VM.enableGC();
138        RVMClass targetClass = Magic.getObjectType(targetObject).asClass();
139        RVMMethod targetMethod = targetClass.findVirtualMethod(methodRef.getName(), methodRef.getDescriptor());
140        if (targetMethod == null) {
141          throw new IncompatibleClassChangeError(targetClass.getDescriptor().classNameFromDescriptor());
142        }
143        return targetMethod;
144      }
145    }
146
147    /**
148     * Compile (if necessary) targetMethod and patch the appropriate dispatch tables.
149     *
150     * @param dynamicLink a DynamicLink that describes call site.
151     * @param targetMethod the RVMMethod to compile (if not already compiled)
152     */
153    @NoInline
154    static void compileMethod(DynamicLink dynamicLink, RVMMethod targetMethod) {
155
156      RVMClass targetClass = targetMethod.getDeclaringClass();
157
158      // if necessary, compile method
159      //
160      if (!targetMethod.isCompiled()) {
161        targetMethod.compile();
162
163        // If targetMethod is a virtual method, then eagerly patch tib of declaring class.
164        // (we need to do this to get the method test used by opt to work with lazy compilation).
165        if (!(targetMethod.isObjectInitializer() || targetMethod.isStatic())) {
166          targetClass.updateTIBEntry(targetMethod);
167        }
168      }
169
170      // patch appropriate dispatch table
171      //
172      if (targetMethod.isObjectInitializer() || targetMethod.isStatic()) {
173        targetClass.updateJTOCEntry(targetMethod);
174      } else if (dynamicLink.isInvokeSpecial()) {
175        targetClass.updateTIBEntry(targetMethod);
176      } else {
177        VM.disableGC();
178        Object targetObject;
179        if (VM.BuildForIA32) {
180          targetObject = org.jikesrvm.ia32.DynamicLinkerHelper.getReceiverObject();
181        } else {
182          if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
183          targetObject = org.jikesrvm.ppc.DynamicLinkerHelper.getReceiverObject();
184        }
185        VM.enableGC();
186        RVMClass recvClass = (RVMClass) Magic.getObjectType(targetObject);
187        recvClass.updateTIBEntry(targetMethod);
188      }
189    }
190  }
191}