diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-07-20 00:32:39 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-07-20 00:32:39 -0700 |
commit | a8bd1e25b9fe32a8eff2f51ee3bfa527440beb84 (patch) | |
tree | e5ce970714407e6269b4d5a1255bc8947c99a4a4 /src | |
parent | cf2a36315f380391a4bdf9f3f608715b392ccca6 (diff) | |
parent | baf3d1a2516e69660cd9a3d6ea3120327885fe93 (diff) | |
download | scala-a8bd1e25b9fe32a8eff2f51ee3bfa527440beb84.tar.gz scala-a8bd1e25b9fe32a8eff2f51ee3bfa527440beb84.tar.bz2 scala-a8bd1e25b9fe32a8eff2f51ee3bfa527440beb84.zip |
Merge pull request #929 from scalamacros/ticket/5895
fixes field mirrors and also improves docs and exceptions for all mirrors
Diffstat (limited to 'src')
-rw-r--r-- | src/library/scala/reflect/package.scala | 3 | ||||
-rw-r--r-- | src/reflect/scala/reflect/api/Mirrors.scala | 144 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/JavaMirrors.scala | 56 |
3 files changed, 150 insertions, 53 deletions
diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 9f9d4089c4..d97f2ec633 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -65,3 +65,6 @@ package object reflect { @deprecated("Use `@scala.beans.ScalaBeanInfo` instead", "2.10.0") type ScalaBeanInfo = scala.beans.ScalaBeanInfo } + +/** An exception that indicates an error during Scala reflection */ +case class ScalaReflectionException(msg: String) extends Exception(msg) diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala index 348ab3656b..27176a2a2d 100644 --- a/src/reflect/scala/reflect/api/Mirrors.scala +++ b/src/reflect/scala/reflect/api/Mirrors.scala @@ -27,28 +27,63 @@ trait Mirrors { self: Universe => /** The instance value reflected by this mirror */ def instance: Any - /** The symbol corresponding to the run-time class of the reflected instance. */ + /** The symbol corresponding to the run-time class of the reflected instance */ def symbol: ClassSymbol - /** Get value of field in reflected instance. - * @field A field symbol that should represent a field of the instance class. - * @return The value associated with that field in the reflected instance - * @throws ??? + /** Reflects against a field symbol and returns a mirror + * that can be used to get and, if appropriate, set the value of the field. + * + * To get a field symbol by the name of the field you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the field>)).asTermSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the class of the instance underlying this mirror. + * + * The input symbol can represent either a field itself or one of the corresponding accessors + * (in all cases the resulting mirror will refer to the field symbol). + * + * If a field symbol doesn't correspond to a reflectable entity of the underlying platform, + * a `ScalaReflectionException` exception will be thrown. This might happen, for example, for primary constructor parameters. + * Typically they produce class fields, however, private parameters that aren't used outside the constructor + * remain plain parameters of a constructor method of the class. */ def reflectField(field: TermSymbol): FieldMirror - /** Invokes a method on the reflected instance. - * @param meth A method symbol that should represent a method of the instance class - * @param args The arguments to pass to the method - * @return The result of invoking `meth(args)` on the reflected instance. - * @throws ??? + /** Reflects against a method symbol and returns a mirror + * that can be used to invoke the method provided. + * + * To get a method symbol by the name of the method you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the method>)).asMethodSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the instance underlying this mirror. */ def reflectMethod(method: MethodSymbol): MethodMirror - /** .. */ + /** Reflects against an inner class symbol and returns a mirror + * that can be used to create instances of the class, inspect its companion object or perform further reflections. + * + * To get a class symbol by the name of the class you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTypeName(<name of the class>)).asClassSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the instance underlying this mirror. + */ def reflectClass(cls: ClassSymbol): ClassMirror - /** .. */ + /** Reflects against an inner module symbol and returns a mirror + * that can be used to get the instance of the object or inspect its companion class. + * + * To get a module symbol by the name of the object you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the object>)).asModuleSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the instance underlying this mirror. + */ def reflectModule(mod: ModuleSymbol): ModuleMirror } @@ -58,13 +93,31 @@ trait Mirrors { self: Universe => /** The object containing the field */ def receiver: AnyRef - /** The field symbol representing the field */ + /** The field symbol representing the field. + * + * In Scala `val` and `var` declarations are usually compiled down to a pair of + * a backing field and corresponding accessor/accessors, which means that a single + * declaration might correspond to up to three different symbols. Nevertheless + * the `FieldMirror.symbol` field always points to a backing field symbol. + */ def symbol: TermSymbol - /** Retrieves the value stored in the field */ + /** Retrieves the value stored in the field. + * + * Scala reflection uses reflection capabilities of the underlying platform, + * so `FieldMirror.get` might throw platform-specific exceptions associated + * with getting a field or invoking a getter method of the field. + */ def get: Any - /** Updates the value stored in the field */ + /** Updates the value stored in the field. + * + * If a field is immutable, a `ScalaReflectionException` will be thrown. + * + * Scala reflection uses reflection capabilities of the underlying platform, + * so `FieldMirror.get` might throw platform-specific exceptions associated + * with setting a field or invoking a setter method of the field. + */ def set(value: Any): Unit } @@ -77,8 +130,12 @@ trait Mirrors { self: Universe => /** The method symbol representing the method */ def symbol: MethodSymbol - /** The result of applying the method to the given arguments */ - // [Eugene+++] If it's a constructor, it should account for inner classes + /** The result of applying the method to the given arguments + * + * Scala reflection uses reflection capabilities of the underlying platform, + * so `FieldMirror.get` might throw platform-specific exceptions associated + * with invoking the corresponding method or constructor. + */ def apply(args: Any*): Any } @@ -97,7 +154,7 @@ trait Mirrors { self: Universe => */ def isStatic: Boolean - /** The Scala symbol corresponding to the reflected runtime class or module. */ + /** The Scala symbol corresponding to the reflected runtime class or object */ def symbol: Symbol /** Optionally, the mirror of the companion reflected by this mirror. @@ -116,7 +173,7 @@ trait Mirrors { self: Universe => /** A mirror that reflects a Scala object definition or the static parts of a runtime class */ trait ModuleMirror extends TemplateMirror { - /** The Scala module symbol corresponding to the reflected module. */ + /** The Scala module symbol corresponding to the reflected object */ override def symbol: ModuleSymbol /** If the reflected runtime class corresponds to a Scala object definition, @@ -137,15 +194,18 @@ trait Mirrors { self: Universe => /** A mirror that reflects the instance parts of a runtime class */ trait ClassMirror extends TemplateMirror { - /** The Scala class symbol corresponding to the reflected class. */ + /** The Scala class symbol corresponding to the reflected class */ override def symbol: ClassSymbol - /** Returns a fresh instance of by invoking that constructor. - * @throws InstantiationException if the class does not have a public - * constructor with an empty parameter list. - * @throws IllegalAccessException if the class or its constructor is not accessible. - * @throws ExceptionInInitializerError if the initialization of the constructor fails. - * @throws SecurityException if creating a new instance is not permitted. + /** Reflects against a constructor symbol and returns a mirror + * that can be used to invoke it and construct instances of this mirror's symbols. + * + * To get a constructor symbol you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(nme.CONSTRUCTOR).asMethodSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the class underlying this mirror. */ def reflectConstructor(constructor: MethodSymbol): MethodMirror @@ -161,24 +221,40 @@ trait Mirrors { self: Universe => /** A mirror that reflects instances and static classes */ trait ReflectiveMirror extends MirrorOf[Mirrors.this.type] { - /** A reflective mirror for the given object - * @param obj An arbitrary value - * @return The mirror for `obj`. + /** A reflective mirror for the given object. + * + * Such a mirror can be used to further reflect against the members of the object + * to get/set fields, invoke methods and inspect inner classes and objects. */ def reflect(obj: Any): InstanceMirror - /** .. */ + /** Reflects against a static class symbol and returns a mirror + * that can be used to create instances of the class, inspect its companion object or perform further reflections. + * + * To get a class symbol by the name of the class you would like to reflect, + * use `<this mirror>.classSymbol(<runtime class loaded by its name>)`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be static, i.e. either top-level or nested within one or several static objects. + */ def reflectClass(cls: ClassSymbol): ClassMirror - /** .. */ + /** Reflects against a static module symbol and returns a mirror + * that can be used to get the instance of the object or inspect its companion class. + * + * To get a module symbol by the name of its companion class you would like to reflect, + * use `<this mirror>.classSymbol(<runtime class loaded by its name>).companion.get`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be static, i.e. either top-level or nested within one or several static objects. + */ def reflectModule(mod: ModuleSymbol): ModuleMirror } /** The API of a mirror for a reflective universe */ trait RuntimeMirror extends ReflectiveMirror { self => - /** Maps a Scala type to the corresponding Java class object - */ + /** Maps a Scala type to the corresponding Java class object */ def runtimeClass(tpe: Type): RuntimeClass /** Maps a Scala class symbol to the corresponding Java class object @@ -198,7 +274,7 @@ trait Mirrors { self: Universe => def classSymbol(rtcls: RuntimeClass): ClassSymbol /** A module symbol for the specified runtime class. - * @return The module symbol for the runtime class in the current class loader. + * @return The module symbol for the runtime class in the current class loader. * @throws java.lang.ClassNotFoundException if no class with that name exists * @throws scala.reflect.internal.MissingRequirementError if no corresponding symbol exists * to do: throws anything else? diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 75d43a7553..9f9f79058d 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -107,15 +107,28 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym // ----------- Implementations of mirror operations and classes ------------------- + private def ErrorInnerClass(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + private def ErrorInnerModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + private def ErrorStaticClass(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + private def ErrorStaticModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + private def ErrorNotMember(wannabe: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${wannabe.kind} ${wannabe.fullName}") + private def ErrorNotField(wannabe: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $wannabe}") + private def ErrorNonExistentField(wannabe: Symbol) = throw new ScalaReflectionException(s""" + |Scala field ${wannabe.name} isn't represented as a Java field, neither it has a Java accessor method + |note that private parameters of class constructors don't get mapped onto fields and/or accessors, + |unless they are used outside of their declaring constructors. + """.trim.stripMargin) + private def ErrorSetImmutableField(wannabe: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${wannabe.name}") + def reflect(obj: Any): InstanceMirror = new JavaInstanceMirror(obj.asInstanceOf[AnyRef]) def reflectClass(cls: ClassSymbol): ClassMirror = { - if (!cls.isStatic) throw new Error("this is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + if (!cls.isStatic) ErrorInnerClass(cls) new JavaClassMirror(null, cls) } def reflectModule(mod: ModuleSymbol): ModuleMirror = { - if (!mod.isStatic) throw new Error("this is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + if (!mod.isStatic) ErrorInnerModule(mod) new JavaModuleMirror(null, mod) } @@ -127,34 +140,39 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym def moduleSymbol(rtcls: RuntimeClass): ModuleSymbol = classToScala(rtcls).companionModule.asModuleSymbol + private def checkMemberOf(wannabe: Symbol, owner: Symbol) = + if (!owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember(wannabe, owner) + private class JavaInstanceMirror(obj: AnyRef) extends InstanceMirror { def instance = obj def symbol = wholemirror.classSymbol(obj.getClass) def reflectField(field: TermSymbol): FieldMirror = { - // [Eugene+++] check whether `field` represents a member of a `symbol` - if (field.isMethod || field.isModule) throw new Error(s""" - |expected a field symbol, you provided a ${field.kind} symbol - |A typical cause of this problem is using a field accessor symbol instead of a field symbol. - |To obtain a field symbol append nme.LOCAL_SUFFIX_STRING to the name of the field, - |when searching for a member with Type.members or Type.declarations. - |This is a temporary inconvenience that will be resolved before 2.10.0-final. - |More information can be found here: https://issues.scala-lang.org/browse/SI-5895. - """.trim.stripMargin) - new JavaFieldMirror(obj, field) + checkMemberOf(field, symbol) + if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field) + val name = + if (field.isGetter) nme.getterToLocal(field.name) + else if (field.isSetter) nme.getterToLocal(nme.setterToGetter(field.name)) + else field.name + val field1 = (field.owner.info decl name).asTermSymbol + try fieldToJava(field1) + catch { + case _: NoSuchFieldException => ErrorNonExistentField(field1) + } + new JavaFieldMirror(obj, field1) } def reflectMethod(method: MethodSymbol): MethodMirror = { - // [Eugene+++] check whether `method` represents a member of a `symbol` + checkMemberOf(method, symbol) new JavaMethodMirror(obj, method) } def reflectClass(cls: ClassSymbol): ClassMirror = { - // [Eugene+++] check whether `cls` represents a member of a `symbol` - if (cls.isStatic) throw new Error("this is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + if (cls.isStatic) ErrorStaticClass(cls) + checkMemberOf(cls, symbol) new JavaClassMirror(instance, cls) } def reflectModule(mod: ModuleSymbol): ModuleMirror = { - // [Eugene+++] check whether `mod` represents a member of a `symbol` - if (mod.isStatic) throw new Error("this is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + if (mod.isStatic) ErrorStaticModule(mod) + checkMemberOf(mod, symbol) new JavaModuleMirror(instance, mod) } } @@ -168,7 +186,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym } def get = jfield.get(receiver) def set(value: Any) = { - if (!symbol.isMutable) throw new Error("cannot set an immutable field") + if (!symbol.isMutable) ErrorSetImmutableField(symbol) jfield.set(receiver, value) } } @@ -186,7 +204,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym case nme.length => jArray.getLength(receiver) case nme.apply => jArray.get(receiver, args(0).asInstanceOf[Int]) case nme.update => jArray.set(receiver, args(0).asInstanceOf[Int], args(1)) - case _ => throw new Error(s"unexpected array method $symbol") + case _ => assert(false, s"unexpected array method: $symbol") } else jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) |