forked from scala/scala
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tunnel class constants to indy bootstrap methods
- Loading branch information
Showing
4 changed files
with
113 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | ||
} | ||
|
||
} |