summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala133
1 files changed, 133 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
new file mode 100644
index 0000000000..da6ac07d6c
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -0,0 +1,133 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author
+ */
+// $Id$
+package scala.tools.nsc.typechecker;
+
+import symtab.Flags._;
+import util.ListBuffer;
+
+/**
+ * - caseArity, caseElement implementations added to case classes
+ * - equals, and hashCode and toString methods are added to case classes,
+ * unless they are defined in the class or a baseclass
+ * different from java.lang.Object
+ * - toString method is added to case objects,
+ * unless they are defined in the class or a baseclass
+ * different from java.lang.Object
+*/
+[_trait_] abstract class SyntheticMethods: Analyzer {
+ import global._; // the global environment
+ import definitions._; // standard classes and methods
+ import typer.{typed}; // methods to type trees
+
+ def addSyntheticMethods(templ: Template, clazz: Symbol): Template = {
+
+ def hasImplementation(name: Name): boolean = {
+ val sym = clazz.info.nonPrivateMember(name);
+ (sym.isTerm &&
+ (sym.owner == clazz ||
+ !(ObjectClass isSubClass sym.owner) && !(sym hasFlag DEFERRED)))
+ }
+
+ def syntheticMethod(name: Name, flags: int, tpe: Type) =
+ newSyntheticMethod(name, flags | OVERRIDE, tpe);
+
+ def newSyntheticMethod(name: Name, flags: int, tpe: Type) = {
+ val method = clazz.newMethod(clazz.pos, name) setFlag (flags) setInfo tpe;
+ clazz.info.decls.enter(method);
+ method
+ }
+
+ def caseElementMethod: Tree = {
+ val method = syntheticMethod(
+ nme.caseElement, FINAL, MethodType(List(IntClass.tpe), AnyClass.tpe));
+ val caseFields = clazz.caseFieldAccessors map gen.mkRef;
+ typed(
+ DefDef(method, vparamss =>
+ if (caseFields.isEmpty) Literal(Constant(null))
+ else {
+ var i = caseFields.length;
+ var cases = List(CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(null))));
+ for (val field <- caseFields.reverse) {
+ i = i - 1; cases = CaseDef(Literal(Constant(i)), EmptyTree, field) :: cases
+ }
+ Match(Ident(vparamss.head.head), cases)
+ }))
+ }
+
+ def caseArityMethod: Tree = {
+ val method = syntheticMethod(nme.caseArity, FINAL, PolyType(List(), IntClass.tpe));
+ typed(DefDef(method, vparamss => Literal(Constant(clazz.caseFieldAccessors.length))))
+ }
+
+ def caseNameMethod: Tree = {
+ val method = syntheticMethod(nme.caseName, FINAL, PolyType(List(), StringClass.tpe));
+ typed(DefDef(method, vparamss => Literal(Constant(clazz.name.decode))))
+ }
+
+ def moduleToStringMethod: Tree = {
+ val method = syntheticMethod(nme.toString_, FINAL, MethodType(List(), StringClass.tpe));
+ typed(DefDef(method, vparamss => Literal(Constant(clazz.name.decode))))
+ }
+
+ def tagMethod: Tree = {
+ val method = syntheticMethod(nme.tag, FINAL, MethodType(List(), IntClass.tpe));
+ typed(DefDef(method, vparamss => Literal(Constant(clazz.tag))))
+ }
+
+ def forwardingMethod(name: Name): Tree = {
+ val target = getMember(ScalaRunTimeModule, "_" + name);
+ val method = syntheticMethod(
+ name, 0, MethodType(target.tpe.paramTypes.tail, target.tpe.resultType));
+ typed(DefDef(method, vparamss =>
+ Apply(gen.mkRef(target), This(clazz) :: (vparamss.head map Ident))));
+ }
+
+ val SerializableAttr = definitions.SerializableAttr;
+
+ def isSerializable(clazz: Symbol): Boolean = {
+ clazz.attributes.exists(p => p match {
+ case Pair(SerializableAttr, _) => true;
+ case _ => false
+ })
+ }
+
+ def readResolveMethod: Tree = {
+ // !!! the synthetic method "readResolve" should be private,
+ // but then it is renamed !!!
+ val method = newSyntheticMethod(nme.readResolve, PROTECTED,
+ MethodType(List(), ObjectClass.tpe));
+ typed(DefDef(method, vparamss => gen.mkRef(clazz.sourceModule)))
+ }
+
+ val ts = new ListBuffer[Tree];
+ if ((clazz hasFlag CASE) && !phase.erasedTypes) {
+ // case classes are implicitly declared serializable
+ clazz.attributes = Pair(SerializableAttr, List()) :: clazz.attributes;
+
+ ts += tagMethod;
+ if (clazz.isModuleClass) {
+ if (!hasImplementation(nme.toString_)) ts += moduleToStringMethod;
+ } else {
+ if (!hasImplementation(nme.hashCode_)) ts += forwardingMethod(nme.hashCode_);
+ if (!hasImplementation(nme.toString_)) ts += forwardingMethod(nme.toString_);
+ if (!hasImplementation(nme.equals_)) ts += forwardingMethod(nme.equals_);
+ }
+ if (!hasImplementation(nme.caseElement)) ts += caseElementMethod;
+ if (!hasImplementation(nme.caseArity)) ts += caseArityMethod;
+ if (!hasImplementation(nme.caseName)) ts += caseNameMethod;
+ }
+ if (!phase.erasedTypes && clazz.isModuleClass && isSerializable(clazz)) {
+ // If you serialize a singleton and then deserialize it twice,
+ // you will have two instances of your singleton, unless you implement
+ // the readResolve() method (see http://www.javaworld.com/javaworld/
+ // jw-04-2003/jw-0425-designpatterns_p.html)
+ if (!hasImplementation(nme.readResolve)) ts += readResolveMethod;
+ }
+ val synthetics = ts.toList;
+ copy.Template(
+ templ, templ.parents, if (synthetics.isEmpty) templ.body else templ.body ::: synthetics)
+ }
+}