Fix for erroneous bytecode generation.

A remedy for an IllegalAccessError where generated bytecode
referred to an inaccessible type.  Closes SI-1430.

Bonus materials:

 - tore out all the invokedynamic support.  The shipped jdk7
 implementation shows limited resemblance to the one this was written
 against; the code mostly serves to distract.  (I think I could get
 invokedynamic working pretty quickly, except that it would
 mean having a codebase for java7 and one for 5-6, which is not a yak
 I wish to shave today.)

 - gave NullClass and NothingClass objects of their own, which
 allowed a nice polymorphic simplification of isSubClass, plus a
 couple other streamlinings.

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@26078 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
extempore 2011-11-28 08:03:10 +00:00
parent da8c20001b
commit 7bce33982a
22 changed files with 136 additions and 247 deletions

Binary file not shown.

View File

@ -172,11 +172,27 @@ trait Definitions extends reflect.api.StandardDefinitions {
lazy val AnyValCompanionClass = getClass("scala.AnyValCompanion") setFlag (SEALED | ABSTRACT | TRAIT)
// bottom types
lazy val NullClass = newClass(ScalaPackageClass, tpnme.Null, anyrefparam) setFlag (ABSTRACT | TRAIT | FINAL)
lazy val NothingClass = newClass(ScalaPackageClass, tpnme.Nothing, anyparam) setFlag (ABSTRACT | TRAIT | FINAL)
lazy val RuntimeNothingClass = getClass(ClassfileConstants.SCALA_NOTHING)
lazy val RuntimeNullClass = getClass(ClassfileConstants.SCALA_NULL)
sealed abstract class BottomClassSymbol(name: TypeName, parent: Symbol) extends ClassSymbol(ScalaPackageClass, NoPosition, name) {
locally {
this setFlag ABSTRACT | TRAIT | FINAL
this setInfo ClassInfoType(List(parent.tpe), new Scope, this)
owner.info.decls enter this
}
final override def isBottomClass = true
}
final object NothingClass extends BottomClassSymbol(tpnme.Nothing, AnyClass) {
override def isSubClass(that: Symbol) = true
}
final object NullClass extends BottomClassSymbol(tpnme.Null, AnyRefClass) {
override def isSubClass(that: Symbol) = (
(that eq AnyClass)
|| (that ne NothingClass) && (that isSubClass ObjectClass)
)
}
// exceptions and other throwables
lazy val ClassCastExceptionClass = getClass("java.lang.ClassCastException")
lazy val IndexOutOfBoundsExceptionClass = getClass(sn.IOOBException)
@ -350,12 +366,6 @@ trait Definitions extends reflect.api.StandardDefinitions {
lazy val ScalaSignatureAnnotation = getClass("scala.reflect.ScalaSignature")
lazy val ScalaLongSignatureAnnotation = getClass("scala.reflect.ScalaLongSignature")
// invoke dynamic support
lazy val LinkageModule = getModule("java.dyn.Linkage")
lazy val Linkage_invalidateCallerClass = getMember(LinkageModule, "invalidateCallerClass")
lazy val DynamicDispatchClass = getModule("scala.runtime.DynamicDispatch")
lazy val DynamicDispatch_DontSetTarget = getMember(DynamicDispatchClass, "DontSetTarget")
// Option classes
lazy val OptionClass: Symbol = getClass("scala.Option")

View File

@ -334,6 +334,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def isTerm = false // to be overridden
def isType = false // to be overridden
def isClass = false // to be overridden
def isBottomClass = false // to be overridden
def isAliasType = false // to be overridden
def isAbstractType = false // to be overridden
private[scala] def isSkolem = false // to be overridden
@ -951,6 +952,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
}
def hasRawInfo: Boolean = infos ne null
def hasCompleteInfo = hasRawInfo && rawInfo.isComplete
/** Return info without checking for initialization or completing */
def rawInfo: Type = {
@ -1236,17 +1238,15 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
owner == that || owner != NoSymbol && (owner isNestedIn that)
/** Is this class symbol a subclass of that symbol? */
final def isNonBottomSubClass(that: Symbol): Boolean =
this == that || this.isError || that.isError ||
final def isNonBottomSubClass(that: Symbol): Boolean = (
(this eq that) || this.isError || that.isError ||
info.baseTypeIndex(that) >= 0
final def isSubClass(that: Symbol): Boolean = (
isNonBottomSubClass(that) ||
this == NothingClass ||
this == NullClass &&
(that == AnyClass ||
that != NothingClass && (that isSubClass ObjectClass))
)
/** Overridden in NullClass and NothingClass for custom behavior.
*/
def isSubClass(that: Symbol) = isNonBottomSubClass(that)
final def isNumericSubClass(that: Symbol): Boolean =
definitions.isNumericSubClass(this, that)
@ -2425,6 +2425,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
unlock()
validTo = currentPeriod
}
override def isSubClass(that: Symbol) = false
override def filter(cond: Symbol => Boolean) = this
override def defString: String = toString
override def locationString: String = ""

View File

@ -3063,8 +3063,7 @@ A type's typeSymbol should never be inspected directly.
case TypeRef(pre, sym, args) if (variance != 0) && (occurCount isDefinedAt sym) =>
val repl = if (variance == 1) dropSingletonType(tp1.bounds.hi) else tp1.bounds.lo
//println("eliminate "+sym+"/"+repl+"/"+occurCount(sym)+"/"+(tparams exists (repl.contains)))//DEBUG
if (repl.typeSymbol != NothingClass && repl.typeSymbol != NullClass &&
occurCount(sym) == 1 && !(tparams exists (repl.contains)))
if (!repl.typeSymbol.isBottomClass && occurCount(sym) == 1 && !(tparams exists (repl.contains)))
repl
else tp1
case _ =>

View File

@ -75,7 +75,7 @@ trait Erasure {
case TypeRef(pre, sym, args) =>
if (sym == ArrayClass)
if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe
else if (args.head.typeSymbol == NothingClass || args.head.typeSymbol == NullClass) arrayType(ObjectClass.tpe)
else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectClass.tpe)
else typeRef(apply(pre), sym, args map this)
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass)
else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass)

View File

@ -24,7 +24,7 @@ abstract class GenICode extends SubComponent {
import icodes._
import icodes.opcodes._
import definitions.{
ArrayClass, ObjectClass, ThrowableClass, StringClass, StringModule, NothingClass, NullClass, AnyRefClass,
ArrayClass, ObjectClass, ThrowableClass, StringClass, StringModule, AnyRefClass,
Object_equals, Object_isInstanceOf, Object_asInstanceOf, ScalaRunTimeModule,
BoxedNumberClass, BoxedCharacterClass,
getMember
@ -923,13 +923,14 @@ abstract class GenICode extends SubComponent {
}
case ApplyDynamic(qual, args) =>
assert(!forMSIL)
ctx.clazz.bootstrapClass = Some("scala.runtime.DynamicDispatch")
val ctx1 = genLoad(qual, ctx, ObjectReference)
genLoadArguments(args, tree.symbol.info.paramTypes, ctx1)
ctx1.bb.emit(CALL_METHOD(tree.symbol, InvokeDynamic), tree.pos)
ctx1
assert(!forMSIL, tree)
// TODO - this is where we'd catch dynamic applies for invokedynamic.
sys.error("No invokedynamic support yet.")
// val ctx1 = genLoad(qual, ctx, ObjectReference)
// genLoadArguments(args, tree.symbol.info.paramTypes, ctx1)
// ctx1.bb.emit(CALL_METHOD(tree.symbol, InvokeDynamic), tree.pos)
// ctx1
case This(qual) =>
assert(tree.symbol == ctx.clazz.symbol || tree.symbol.isModuleClass,
"Trying to access the this of another class: " +

View File

@ -106,7 +106,6 @@ trait Members { self: ICodes =>
var fields: List[IField] = Nil
var methods: List[IMethod] = Nil
var cunit: CompilationUnit = _
var bootstrapClass: Option[String] = None
def addField(f: IField): this.type = {
fields = f :: fields;

View File

@ -616,7 +616,6 @@ trait Opcodes { self: ICodes =>
/** Returns a string representation of this style. */
override def toString(): String = this match {
case Dynamic => "dynamic"
case InvokeDynamic => "invoke-dynamic"
case Static(false) => "static-class"
case Static(true) => "static-instance"
case SuperCall(mix) => "super(" + mix + ")"
@ -626,9 +625,6 @@ trait Opcodes { self: ICodes =>
/** Virtual calls */
case object Dynamic extends InvokeStyle
/** InvokeDynamic a la JSR 292 (experimental). */
case object InvokeDynamic extends InvokeStyle
/**
* Special invoke. Static(true) is used for calls to private
* members.

View File

@ -321,7 +321,7 @@ abstract class CopyPropagation {
out.stack = Unknown :: out.stack.drop(i.consumed)
case CALL_METHOD(method, style) => style match {
case Dynamic | InvokeDynamic =>
case Dynamic =>
out = simulateCall(in, method, false)
case Static(onInstance) =>

View File

@ -31,7 +31,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
import icodes._
import icodes.opcodes._
import definitions.{
NullClass, RuntimeNullClass, NothingClass, RuntimeNothingClass,
AnyClass, ObjectClass, ThrowsClass, ThrowableClass, ClassfileAnnotationClass,
SerializableClass, StringClass, ClassClass, FunctionClass,
DeprecatedAttr, SerializableAttr, SerialVersionUIDAttr, VolatileAttr,
@ -332,8 +331,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
ifaces,
c.cunit.source.toString)
if (isStaticModule(c.symbol) || serialVUID != None || isParcelableClass ||
clasz.bootstrapClass.isDefined) {
if (isStaticModule(c.symbol) || serialVUID != None || isParcelableClass) {
if (isStaticModule(c.symbol))
addModuleInstanceField
addStaticInit(jclass, c.lookupStaticCtor)
@ -375,9 +373,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
}
}
if (clasz.bootstrapClass.isDefined)
jclass setBootstrapClass clasz.bootstrapClass.get
clasz.fields foreach genField
clasz.methods foreach genMethod
@ -913,8 +908,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
mopt match {
case Some(m) =>
if (clasz.bootstrapClass.isDefined) legacyEmitBootstrapMethodInstall(clinit)
val oldLastBlock = m.code.blocks.last
val lastBlock = m.code.newBlock
oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock))
@ -940,11 +933,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
if (isParcelableClass)
addCreatorCode(BytecodeGenerator.this, lastBlock)
if (clasz.bootstrapClass.isDefined) {
// emit bootstrap method install
//emitBootstrapMethodInstall(block)
}
lastBlock emit RETURN(UNIT)
lastBlock.close
@ -975,28 +963,9 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
if (isParcelableClass)
legacyAddCreatorCode(BytecodeGenerator.this, clinit)
if (clasz.bootstrapClass.isDefined)
legacyEmitBootstrapMethodInstall(clinit)
clinit.emitRETURN()
}
/** Emit code that installs a boostrap method for invoke dynamic. It
* installs the default method, found in scala.runtime.DynamicDispatch.
*/
def legacyEmitBootstrapMethodInstall(jcode: JExtendedCode) {
jcode emitPUSH jclass.getType.asInstanceOf[JReferenceType]
jcode emitPUSH new JObjectType("scala.runtime.DynamicDispatch")
jcode emitPUSH "bootstrapInvokeDynamic"
jcode.emitGETSTATIC("java.dyn.Linkage", "BOOTSTRAP_METHOD_TYPE", MethodTypeType)
jcode.emitDUP
jcode.emitINVOKESTATIC("scala.Console", "println", new JMethodType(JType.VOID, Array(JAVA_LANG_OBJECT)))
jcode.emitINVOKESTATIC("java.dyn.MethodHandles", "findStatic",
new JMethodType(MethodHandleType, Array(JavaLangClassType, JAVA_LANG_STRING, MethodTypeType)))
jcode.emitINVOKESTATIC("java.dyn.Linkage", "registerBootstrapMethod",
new JMethodType(JType.VOID, Array(JavaLangClassType, MethodHandleType)))
}
/** Add a forwarder for method m */
def addForwarder(jclass: JClass, module: Symbol, m: Symbol) {
val moduleName = javaName(module)
@ -1196,6 +1165,68 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
log("Empty exception range: " + p)
}
}
def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = {
target.isPublic || target.isProtected && {
(site.enclClass isSubClass target.enclClass) ||
(site.enclosingPackage == target.privateWithin)
}
}
def genCallMethod(call: CALL_METHOD) {
val CALL_METHOD(method, style) = call
val siteSymbol = clasz.symbol
val hostSymbol = call.hostClass
val methodOwner = method.owner
// info calls so that types are up to date; erasure may add lateINTERFACE to traits
hostSymbol.info ; methodOwner.info
def isInterfaceCall(sym: Symbol) = (
sym.isInterface
|| sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass)
)
// whether to reference the type of the receiver or
// the type of the method owner (if not an interface!)
val useMethodOwner = (
style != Dynamic
|| !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol)
|| hostSymbol.isBottomClass
)
val receiver = if (useMethodOwner) methodOwner else hostSymbol
val jowner = javaName(receiver)
val jname = javaName(method)
val jtype = javaType(method).asInstanceOf[JMethodType]
def emit(invoke: String) {
log("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype))
invoke match {
case "invokeinterface" => jcode.emitINVOKEINTERFACE(jowner, jname, jtype)
case "invokevirtual" => jcode.emitINVOKEVIRTUAL(jowner, jname, jtype)
case "invokespecial" => jcode.emitINVOKESPECIAL(jowner, jname, jtype)
case "invokestatic" => jcode.emitINVOKESTATIC(jowner, jname, jtype)
}
}
def initModule() {
// we initialize the MODULE$ field immediately after the super ctor
if (isStaticModule(siteSymbol) && !isModuleInitialized &&
jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME &&
jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) {
isModuleInitialized = true
jcode.emitALOAD_0()
jcode.emitPUTSTATIC(jclass.getName(),
nme.MODULE_INSTANCE_FIELD.toString,
jclass.getType())
}
}
style match {
case Static(true) => emit("invokespecial")
case Static(false) => emit("invokestatic")
case Dynamic if isInterfaceCall(receiver) => emit("invokeinterface")
case Dynamic => emit("invokevirtual")
case SuperCall(_) => emit("invokespecial") ; initModule()
}
}
def genBlock(b: BasicBlock) {
labels(b).anchorToNext()
@ -1276,43 +1307,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType)
case call @ CALL_METHOD(method, style) =>
val owner: String = javaName(method.owner)
// reference the type of the receiver instead of the method owner (if not an interface!)
val dynamicOwner =
if (needsInterfaceCall(call.hostClass)) owner
else javaName(call.hostClass)
val jname = javaName(method)
val jtype = javaType(method).asInstanceOf[JMethodType]
style match {
case InvokeDynamic =>
jcode.emitINVOKEINTERFACE("java.dyn.Dynamic", jname, jtype)
case Dynamic =>
if (needsInterfaceCall(method.owner))
jcode.emitINVOKEINTERFACE(owner, jname, jtype)
else
jcode.emitINVOKEVIRTUAL(dynamicOwner, jname, jtype)
case Static(instance) =>
if (instance)
jcode.emitINVOKESPECIAL(owner, jname, jtype)
else
jcode.emitINVOKESTATIC(owner, jname, jtype)
case SuperCall(_) =>
jcode.emitINVOKESPECIAL(owner, jname, jtype)
// we initialize the MODULE$ field immediately after the super ctor
if (isStaticModule(clasz.symbol) && !isModuleInitialized &&
jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME &&
jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) {
isModuleInitialized = true
jcode.emitALOAD_0()
jcode.emitPUTSTATIC(jclass.getName(),
nme.MODULE_INSTANCE_FIELD.toString,
jclass.getType())
}
}
genCallMethod(call)
case BOX(kind) =>
val boxedType = definitions.boxedClass(kind.toType.typeSymbol)
@ -1839,20 +1834,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
////////////////////// Utilities ////////////////////////
/** Calls to methods in 'sym' need invokeinterface? */
def needsInterfaceCall(sym: Symbol): Boolean = {
debuglog("checking for interface call: " + sym.fullName)
// the following call to 'info' may cause certain symbols to fail loading
// because we're too late in the compilation chain (aliases to overloaded
// symbols will not be properly resolved, see scala.Range, method
// `super$++` that fails in UnPickler at LazyTypeRefAndAlias.complete
if (sym.isTrait) sym.info // needed so that the type is up to date
// (erasure may add lateINTERFACE to traits)
sym.isInterface ||
(sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass))
}
/** Merge adjacent ranges. */
private def mergeEntries(ranges: List[(Int, Int)]): List[(Int, Int)] =
(ranges.foldLeft(Nil: List[(Int, Int)]) { (collapsed: List[(Int, Int)], p: (Int, Int)) => (collapsed, p) match {

View File

@ -15,7 +15,7 @@ trait CompletionOutput {
val global: Global
import global._
import definitions.{ NothingClass, AnyClass, isTupleTypeOrSubtype, isFunctionType, isRepeatedParamType }
import definitions.{ isTupleTypeOrSubtype, isFunctionType, isRepeatedParamType }
/** Reducing fully qualified noise for some common packages.
*/

View File

@ -25,7 +25,7 @@ trait ParallelMatching extends ast.TreeDSL
import global.{ typer => _, _ }
import definitions.{
AnyRefClass, NothingClass, IntClass, BooleanClass, SomeClass, OptionClass,
AnyRefClass, IntClass, BooleanClass, SomeClass, OptionClass,
getProductArgs, productProj, Object_eq, Any_asInstanceOf
}
import CODE._

View File

@ -131,7 +131,7 @@ trait SymbolTrackers {
else " (" + Flags.flagsToString(masked) + ")"
}
def symString(sym: Symbol) = (
if (settings.debug.value && sym.hasRawInfo && sym.rawInfo.isComplete) {
if (settings.debug.value && sym.hasCompleteInfo) {
val s = sym.defString take 240
if (s.length == 240) s + "..." else s
}

View File

@ -1257,7 +1257,7 @@ abstract class ClassfileParser {
protected def getScope(flags: Int): Scope =
if (isStatic(flags)) staticDefs else instanceDefs
private def setPrivateWithin(sym: Symbol, jflags: Int) {
private def setPrivateWithin(sym: Symbol, jflags: Int) {
if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0)
// See ticket #1687 for an example of when topLevelClass is NoSymbol: it
// apparently occurs when processing v45.3 bytecode.

View File

@ -1033,8 +1033,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
&& qual.isTerm
&& ((qual.symbol eq null) || !qual.symbol.isTerm || qual.symbol.isValue)
&& !qtpe.isError
&& qtpe.typeSymbol != NullClass
&& qtpe.typeSymbol != NothingClass
&& !qtpe.typeSymbol.isBottomClass
&& qtpe != WildcardType
&& !qual.isInstanceOf[ApplyImplicitView] // don't chain views
&& context.implicitsEnabled
@ -2190,10 +2189,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
* in an argument closure overlaps with an uninstantiated formal?
*/
def needsInstantiation(tparams: List[Symbol], formals: List[Type], args: List[Tree]) = {
def isLowerBounded(tparam: Symbol) = {
val losym = tparam.info.bounds.lo.typeSymbol
losym != NothingClass && losym != NullClass
}
def isLowerBounded(tparam: Symbol) = !tparam.info.bounds.lo.typeSymbol.isBottomClass
(formals, args).zipped exists {
case (formal, Function(vparams, _)) =>
(vparams exists (_.tpt.isEmpty)) &&

View File

@ -48,7 +48,6 @@ public class JAttributeFactory {
Constructor defaultConstructor) {
this.context = context;
this.defaultConstructor = defaultConstructor;
registerClass("BootstrapInvokeDynamic", JBootstrapInvokeDynamic.class);
registerClass("Code", JCodeAttribute.class);
registerClass("ConstantValue", JConstantValueAttribute.class);
registerClass("EnclosingMethod", JEnclosingMethodAttribute.class);

View File

@ -1,69 +0,0 @@
/* FJBG -- Fast Java Bytecode Generator
* Copyright 2002-2011 LAMP/EPFL
* @author Michel Schinz
*/
package ch.epfl.lamp.fjbg;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Iterator;
/**
* BootstrapInvokeDynamic entry, as described by JSR 292 (invoke dynamic)
*
* @author Iulian Dragos
* @version 1.0
*
*/
public class JBootstrapInvokeDynamic extends JAttribute {
/** Constant pool of the current classfile. */
private JConstantPool pool;
protected final int classIdx;
public JBootstrapInvokeDynamic(FJBGContext context,
JClass clazz,
String className) {
super(context, clazz);
this.pool = clazz.pool;
this.classIdx = pool.addClass(className);
}
public JBootstrapInvokeDynamic(FJBGContext context,
JClass clazz,
Object owner,
String name,
int size,
DataInputStream stream)
throws IOException {
super(context, clazz, name);
this.classIdx = stream.readShort();
assert name.equals(getName());
}
public String getName() { return "BootstrapInvokeDynamic"; }
// Follows javap output format for BootstrapInvokeDynamic attribute.
/*@Override*/ public String toString() {
StringBuffer buf = new StringBuffer(" BootstrapInvokeDynamic:");
buf.append("\n #");
buf.append(classIdx);
buf.append("; // class ");
buf.append(pool.lookupClass(classIdx));
buf.append("\n");
return buf.toString();
}
protected int getSize() {
return 2; // Short.SIZE
}
protected void writeContentsTo(DataOutputStream stream) throws IOException {
stream.writeShort(classIdx);
}
}

View File

@ -26,8 +26,6 @@ public class JClass extends JMember {
protected final String sourceFileName;
protected final JConstantPool pool;
protected JBootstrapInvokeDynamic bootstrapClassAttr = null;
public final static String[] NO_INTERFACES = new String[0];
protected final LinkedList/*<JMethod>*/ methods = new LinkedList();
@ -306,12 +304,6 @@ public class JClass extends JMember {
bStream.close();
fStream.close();
}
public void setBootstrapClass(String bootstrapClass) {
assert bootstrapClassAttr == null;
bootstrapClassAttr = new JBootstrapInvokeDynamic(context, this, bootstrapClass);
addAttribute(bootstrapClassAttr);
}
/**
* Writes the contents of the class to a data stream.

View File

@ -1,42 +0,0 @@
package scala.runtime;
import java.dyn.CallSite;
import java.dyn.MethodHandle;
/**
* This class resolves calls through refinement types. The
* bootstrap method is called when an invokedynamic is found
* by the Java VM.
*
* Note: Requires Java 7 with invoke dynamic support (see JSR 292)
*
* @author Iulian Dragos
* @see JSR292
*/
public class DynamicDispatch {
/**
* Resolve an invoke dynamic in Scala code. invokedynamic calls appear
* when a method defined by a refinement type is called. It is resolved
* by looking up a method with the same name and types in the receiver
* object. It is guaranteed by the type checker that such a method
* exists.
*
* The current implementation is not correct, a call site being
* always bootstrapped to a method handle. A bound call site should be
* guarded by a test on the receiver type. Such code should either
* be generated by the compiler, or by this bootstrap method using
* one of the code combinators provided in java.dyn.*.
*
* ATM, they are not yet available in the JVM.
*/
public static Object bootstrapInvokeDynamic(CallSite cs, Object... args) {
println(cs);
MethodHandle mh = MethodHandles.findVirtual(cs.callerClass(),
cs.name(),
cs.type());
cs.setTarget(mh);
return mh(args);
}
}

View File

@ -0,0 +1 @@
Baz

View File

@ -0,0 +1,8 @@
package j;
interface Foo {
public void foo();
}
public interface Bar_1 extends Foo {
public void bar();
}

View File

@ -0,0 +1,16 @@
package s {
object Boop extends j.Bar_1 {
def foo() {}
def bar() {}
}
class Baz(x: j.Bar_1) {
x.foo
override def toString = "Baz"
}
}
object Test {
def main(args: Array[String]): Unit = {
println(new s.Baz(s.Boop))
}
}