Skip to content

Commit

Permalink
Tunnel class constants to indy bootstrap methods
Browse files Browse the repository at this point in the history
  • Loading branch information
retronym committed Aug 2, 2016
1 parent 25b29ea commit a95f03e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}

def bootstrapMethodArg(t: Constant, pos: Position): AnyRef = t match {
case Constant(mt: Type) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
case Constant(mt: MethodType) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
case Constant(mt: NullaryMethodType) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
case Constant(tp: Type) => typeToBType(transformedType(tp)).toASMType
case c @ Constant(sym: Symbol) => staticHandleFromSymbol(sym)
case c @ Constant(value: String) => value
case c @ Constant(value) if c.isNonUnitAnyVal => c.value.asInstanceOf[AnyRef]
Expand Down
41 changes: 41 additions & 0 deletions test/files/run/indy-via-macro-3/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package test;

import java.lang.invoke.*;

public final class Bootstrap {
private Bootstrap() {
}
private static final Object U = getUnsafeOrNull();
private static final MethodType OBJECT_FIELD_OFFSET_DESC =
MethodType.methodType(Long.TYPE, java.lang.reflect.Field.class);
private static final MethodType UNSAFE_CAS_INT_DESC =
MethodType.methodType(java.lang.Boolean.TYPE, Object.class, Long.TYPE, Integer.TYPE, Integer.TYPE);

/** Pre-compile a regex */
public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
MethodType invokedType,
Class<?> cls, String fieldName) throws Throwable {
if (U != null) {
MethodHandle objectFieldOffset = MethodHandles.lookup().findVirtual(U.getClass(), "objectFieldOffset", OBJECT_FIELD_OFFSET_DESC);
long offset = (long) objectFieldOffset.invokeExact(cls.getDeclaredField(fieldName));
MethodHandle cas = MethodHandles.lookup().findVirtual(U.getClass(), "compareAndSwapInt", UNSAFE_CAS_INT_DESC);
// [perform access checks]
return new ConstantCallSite(MethodHandles.insertArguments(cas.bindTo(U), 1, offset));
} else {
throw new UnsupportedOperationException("TODO: Detect and use VarHandle");
}
}

@SuppressWarnings("restriction")
private static Object getUnsafeOrNull() {
try {

java.lang.reflect.Field singleoneInstanceField = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
singleoneInstanceField.setAccessible(true);
return singleoneInstanceField.get(null);

} catch (Throwable e) {
return null;
}
}
}
15 changes: 15 additions & 0 deletions test/files/run/indy-via-macro-3/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Macro._

object Test {
def main(args: Array[String]) {
val f = new Foo()
compareAndSetInt(classOf[Foo], "blah", f, -1, 0)
assert(!compareAndSetInt(classOf[Foo], "blah", f, -1, 0))
assert(compareAndSetInt(classOf[Foo], "blah", f, 42, 0))
assert(f.blah == 0)
}
}

class Foo {
var blah = 42
}
54 changes: 54 additions & 0 deletions test/files/run/indy-via-macro-3/macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import java.util.regex.{Pattern, PatternSyntaxException}

import scala.language.experimental.macros
import scala.reflect.internal.SymbolTable
import scala.reflect.macros.blackbox._

object Macro {
def compareAndSetInt(cls: Class[_], field: String, value: AnyRef, expect: Int, update: Int): Boolean = macro impl.compareAndSetInt

class impl(val c: Context) extends IndySupport {
import c.universe._, c.internal.reificationSupport.MethodType

val boostrapSym = typeOf[test.Bootstrap].companion.member(TermName("bootstrap"))
assert(boostrapSym != NoSymbol)
val invokedType = newMethodType(List(typeOf[Object], typeOf[Int], typeOf[Int]), typeOf[Boolean])

def compareAndSetInt(cls: Tree, field: Tree, value: Tree, expect: Tree, update: Tree): Tree = {
(cls, field) match {
case (c@Literal(Constant(cValue: Type)), l@Literal(Constant(_: String))) =>
Indy(boostrapSym, List(c, l), List(value, expect, update), invokedType)
case _ =>
c.abort(c.macroApplication.pos, "cls and field params must be literals")
}
}
}
}

trait IndySupport {
val c: Context
import c.universe._
def newMethodType(paramTps: List[Type], resTp: Type): MethodType = {
val symtab = c.universe.asInstanceOf[SymbolTable]
val paramTps1 = paramTps.asInstanceOf[List[symtab.Type]]
val resTp1 = resTp.asInstanceOf[symtab.Type]
val params1 = paramTps1.zipWithIndex.map {
case (tp, i) =>
val param = symtab.NoSymbol.newValueParameter(symtab.TermName("param$" + i), c.macroApplication.pos.focus.asInstanceOf[symtab.Position])
param.setInfo(tp)
}
symtab.MethodType(params1, resTp1).asInstanceOf[c.universe.MethodType]
}

def Indy(bootstrapMethod: Symbol, bootstrapArgs: List[Literal], dynArgs: List[Tree], invokedType: Type, name: TermName = TermName("dummy")): Tree = {
val symtab = c.universe.asInstanceOf[SymbolTable]
val result = {
import symtab._
val dummySymbol = NoSymbol.newTermSymbol(name.asInstanceOf[symtab.TermName]).setInfo(invokedType.asInstanceOf[symtab.Type])
val args: List[Tree] = Literal(Constant(bootstrapMethod)).setType(NoType) :: bootstrapArgs.asInstanceOf[List[Tree]]
ApplyDynamic(Ident(dummySymbol).setType(dummySymbol.info), args ::: dynArgs.asInstanceOf[List[Tree]]).setType(invokedType.resultType.asInstanceOf[symtab.Type])
}
result.asInstanceOf[Tree]
}

}

0 comments on commit a95f03e

Please sign in to comment.