Skip to content

Commit a95f03e

Browse files
committed
Tunnel class constants to indy bootstrap methods
1 parent 25b29ea commit a95f03e

File tree

4 files changed

+113
-1
lines changed

4 files changed

+113
-1
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
147147
}
148148

149149
def bootstrapMethodArg(t: Constant, pos: Position): AnyRef = t match {
150-
case Constant(mt: Type) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
150+
case Constant(mt: MethodType) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
151+
case Constant(mt: NullaryMethodType) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
152+
case Constant(tp: Type) => typeToBType(transformedType(tp)).toASMType
151153
case c @ Constant(sym: Symbol) => staticHandleFromSymbol(sym)
152154
case c @ Constant(value: String) => value
153155
case c @ Constant(value) if c.isNonUnitAnyVal => c.value.asInstanceOf[AnyRef]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package test;
2+
3+
import java.lang.invoke.*;
4+
5+
public final class Bootstrap {
6+
private Bootstrap() {
7+
}
8+
private static final Object U = getUnsafeOrNull();
9+
private static final MethodType OBJECT_FIELD_OFFSET_DESC =
10+
MethodType.methodType(Long.TYPE, java.lang.reflect.Field.class);
11+
private static final MethodType UNSAFE_CAS_INT_DESC =
12+
MethodType.methodType(java.lang.Boolean.TYPE, Object.class, Long.TYPE, Integer.TYPE, Integer.TYPE);
13+
14+
/** Pre-compile a regex */
15+
public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
16+
MethodType invokedType,
17+
Class<?> cls, String fieldName) throws Throwable {
18+
if (U != null) {
19+
MethodHandle objectFieldOffset = MethodHandles.lookup().findVirtual(U.getClass(), "objectFieldOffset", OBJECT_FIELD_OFFSET_DESC);
20+
long offset = (long) objectFieldOffset.invokeExact(cls.getDeclaredField(fieldName));
21+
MethodHandle cas = MethodHandles.lookup().findVirtual(U.getClass(), "compareAndSwapInt", UNSAFE_CAS_INT_DESC);
22+
// [perform access checks]
23+
return new ConstantCallSite(MethodHandles.insertArguments(cas.bindTo(U), 1, offset));
24+
} else {
25+
throw new UnsupportedOperationException("TODO: Detect and use VarHandle");
26+
}
27+
}
28+
29+
@SuppressWarnings("restriction")
30+
private static Object getUnsafeOrNull() {
31+
try {
32+
33+
java.lang.reflect.Field singleoneInstanceField = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe");
34+
singleoneInstanceField.setAccessible(true);
35+
return singleoneInstanceField.get(null);
36+
37+
} catch (Throwable e) {
38+
return null;
39+
}
40+
}
41+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Macro._
2+
3+
object Test {
4+
def main(args: Array[String]) {
5+
val f = new Foo()
6+
compareAndSetInt(classOf[Foo], "blah", f, -1, 0)
7+
assert(!compareAndSetInt(classOf[Foo], "blah", f, -1, 0))
8+
assert(compareAndSetInt(classOf[Foo], "blah", f, 42, 0))
9+
assert(f.blah == 0)
10+
}
11+
}
12+
13+
class Foo {
14+
var blah = 42
15+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import java.util.regex.{Pattern, PatternSyntaxException}
2+
3+
import scala.language.experimental.macros
4+
import scala.reflect.internal.SymbolTable
5+
import scala.reflect.macros.blackbox._
6+
7+
object Macro {
8+
def compareAndSetInt(cls: Class[_], field: String, value: AnyRef, expect: Int, update: Int): Boolean = macro impl.compareAndSetInt
9+
10+
class impl(val c: Context) extends IndySupport {
11+
import c.universe._, c.internal.reificationSupport.MethodType
12+
13+
val boostrapSym = typeOf[test.Bootstrap].companion.member(TermName("bootstrap"))
14+
assert(boostrapSym != NoSymbol)
15+
val invokedType = newMethodType(List(typeOf[Object], typeOf[Int], typeOf[Int]), typeOf[Boolean])
16+
17+
def compareAndSetInt(cls: Tree, field: Tree, value: Tree, expect: Tree, update: Tree): Tree = {
18+
(cls, field) match {
19+
case (c@Literal(Constant(cValue: Type)), l@Literal(Constant(_: String))) =>
20+
Indy(boostrapSym, List(c, l), List(value, expect, update), invokedType)
21+
case _ =>
22+
c.abort(c.macroApplication.pos, "cls and field params must be literals")
23+
}
24+
}
25+
}
26+
}
27+
28+
trait IndySupport {
29+
val c: Context
30+
import c.universe._
31+
def newMethodType(paramTps: List[Type], resTp: Type): MethodType = {
32+
val symtab = c.universe.asInstanceOf[SymbolTable]
33+
val paramTps1 = paramTps.asInstanceOf[List[symtab.Type]]
34+
val resTp1 = resTp.asInstanceOf[symtab.Type]
35+
val params1 = paramTps1.zipWithIndex.map {
36+
case (tp, i) =>
37+
val param = symtab.NoSymbol.newValueParameter(symtab.TermName("param$" + i), c.macroApplication.pos.focus.asInstanceOf[symtab.Position])
38+
param.setInfo(tp)
39+
}
40+
symtab.MethodType(params1, resTp1).asInstanceOf[c.universe.MethodType]
41+
}
42+
43+
def Indy(bootstrapMethod: Symbol, bootstrapArgs: List[Literal], dynArgs: List[Tree], invokedType: Type, name: TermName = TermName("dummy")): Tree = {
44+
val symtab = c.universe.asInstanceOf[SymbolTable]
45+
val result = {
46+
import symtab._
47+
val dummySymbol = NoSymbol.newTermSymbol(name.asInstanceOf[symtab.TermName]).setInfo(invokedType.asInstanceOf[symtab.Type])
48+
val args: List[Tree] = Literal(Constant(bootstrapMethod)).setType(NoType) :: bootstrapArgs.asInstanceOf[List[Tree]]
49+
ApplyDynamic(Ident(dummySymbol).setType(dummySymbol.info), args ::: dynArgs.asInstanceOf[List[Tree]]).setType(invokedType.resultType.asInstanceOf[symtab.Type])
50+
}
51+
result.asInstanceOf[Tree]
52+
}
53+
54+
}

0 commit comments

Comments
 (0)