/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.runtime;
import org.jikesrvm.SubordinateArchitecture;
import org.jikesrvm.VM_CodeArray;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Constants;
import org.jikesrvm.classloader.VM_Class;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_MethodReference;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.common.VM_CompiledMethods;
import org.vmmagic.pragma.DynamicBridge;
import org.vmmagic.pragma.Entrypoint;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.LocalAddress;
import org.vmmagic.unboxed.Offset;
/**
* Implement lazy compilation.
*/
@DynamicBridge
public class VM_DynamicLinker implements VM_Constants {
/**
* Resolve, compile if necessary, and invoke a method.
* Taken: nothing (calling context is implicit)
* Returned: does not return (method dispatch table is updated and method is executed)
*/
@Entrypoint
static void lazyMethodInvoker() {
VM_DynamicLink dl = DL_Helper.resolveDynamicInvocation();
boolean forSubArch = VM_Magic.runningOnSubArch();
VM_Method targMethod = DL_Helper.resolveMethodRef(dl, forSubArch);
DL_Helper.compileMethod(dl, targMethod, forSubArch);
VM_CodeArray code = targMethod.getCurrentEntryCodeArray(forSubArch);
VM_Magic.dynamicBridgeTo(code); // restore parameters and invoke
if (VM.VerifyAssertions) VM._assert(NOT_REACHED); // does not return here
}
/**
* Report unimplemented native method error.
* Taken: nothing (calling context is implicit)
* Returned: does not return (throws UnsatisfiedLinkError)
*/
@Entrypoint
static void unimplementedNativeMethod() {
VM_DynamicLink dl = DL_Helper.resolveDynamicInvocation();
boolean forSubArch = VM_Magic.runningOnSubArch();
VM_Method targMethod = DL_Helper.resolveMethodRef(dl, forSubArch);
throw new UnsatisfiedLinkError(targMethod.toString());
}
/**
* Report a magic SysCall has been mistakenly invoked
*/
@Entrypoint
static void sysCallMethod() {
VM_DynamicLink dl = DL_Helper.resolveDynamicInvocation();
boolean forSubArch = VM_Magic.runningOnSubArch();
VM_Method targMethod = DL_Helper.resolveMethodRef(dl, forSubArch);
throw new UnsatisfiedLinkError(targMethod.toString() + " which is a SysCall");
}
/**
* Helper class that does the real work of resolving method references
* and compiling a lazy method invocation. In separate class so
* that it doesn't implement DynamicBridge magic.
*/
private static class DL_Helper {
/**
* Discover method reference to be invoked via dynamic bridge.
*
* Taken: nothing (call stack is examined to find invocation site)
* Returned: VM_DynamicLink that describes call site.
*/
@NoInline
static VM_DynamicLink resolveDynamicInvocation() {
// find call site
//
VM.disableGC();
LocalAddress callingFrame = VM_Magic.getCallerFramePointer(VM_Magic.getFramePointer());
LocalAddress returnAddress = VM_Magic.getReturnAddress(callingFrame);
callingFrame = VM_Magic.getCallerFramePointer(callingFrame);
int callingCompiledMethodId = VM_Magic.getCompiledMethodID(callingFrame);
VM_CompiledMethod callingCompiledMethod = VM_CompiledMethods.getCompiledMethod(callingCompiledMethodId);
Offset callingInstructionOffset = callingCompiledMethod.getInstructionOffset(returnAddress);
VM.enableGC();
// obtain symbolic method reference
//
VM_DynamicLink dynamicLink = new VM_DynamicLink();
callingCompiledMethod.getDynamicLink(dynamicLink, callingInstructionOffset);
return dynamicLink;
}
/**
* Resolve method ref into appropriate VM_Method
*
* Taken: VM_DynamicLink that describes call site.
* Returned: VM_Method that should be invoked.
*/
@NoInline
static VM_Method resolveMethodRef(VM_DynamicLink dynamicLink, boolean forSubArch) {
// resolve symbolic method reference into actual method
//
VM_MethodReference methodRef = dynamicLink.methodRef();
if (dynamicLink.isInvokeSpecial()) {
return methodRef.resolveInvokeSpecial(forSubArch);
} else if (dynamicLink.isInvokeStatic()) {
return methodRef.resolve(forSubArch);
} else {
// invokevirtual or invokeinterface
Object targetObject;
VM.disableGC();
if (!VM_Magic.runningOnSubArch()) {
targetObject = ArchitectureSpecific.VM_DynamicLinkerHelper.getReceiverObject();
} else {
targetObject = SubordinateArchitecture.VM_DynamicLinkerHelper.getReceiverObject();
}
VM.enableGC();
VM_Class targetClass = VM_Magic.getObjectType(targetObject).asClass();
VM_Method targetMethod = targetClass.findVirtualMethod(methodRef.getName(), methodRef.getDescriptor());
if (targetMethod == null) {
throw new IncompatibleClassChangeError(targetClass.getDescriptor().classNameFromDescriptor());
}
return targetMethod;
}
}
/**
* Compile (if necessary) targetMethod and patch the appropriate disaptch tables
* @param targetMethod the VM_Method to compile (if not already compiled)
*/
@NoInline
static void compileMethod(VM_DynamicLink dynamicLink, VM_Method targetMethod, boolean forSubArch) {
VM_Class targetClass = targetMethod.getDeclaringClass();
// if necessary, compile method
//
if (!targetMethod.isCompiled(forSubArch)) {
targetMethod.compile(forSubArch);
// If targetMethod is a virtual method, then eagerly patch tib of declaring class.
// (we need to do this to get the method test used by opt to work with lazy compilation).
if (!(targetMethod.isObjectInitializer() || targetMethod.isStatic())) {
targetClass.updateTIBEntry(targetMethod, forSubArch);
}
}
// patch appropriate dispatch table
//
if (targetMethod.isObjectInitializer() || targetMethod.isStatic()) {
targetClass.updateJTOCEntry(targetMethod, forSubArch);
} else if (dynamicLink.isInvokeSpecial()) {
targetClass.updateTIBEntry(targetMethod, forSubArch);
} else {
Object targetObject;
VM.disableGC();
if (!forSubArch) {
targetObject = ArchitectureSpecific.VM_DynamicLinkerHelper.getReceiverObject();
} else {
targetObject = SubordinateArchitecture.VM_DynamicLinkerHelper.getReceiverObject();
}
VM.enableGC();
VM_Class recvClass = (VM_Class) VM_Magic.getObjectType(targetObject);
recvClass.updateTIBEntry(targetMethod, forSubArch);
}
}
}
}