Skip to content

Commit

Permalink
Added delegate support
Browse files Browse the repository at this point in the history
Credits: Most of the work was done by Martin Rubli as a semester project
  • Loading branch information
mihaylov committed Jul 7, 2005
1 parent 5e728c6 commit a481860
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 39 deletions.
27 changes: 27 additions & 0 deletions sources/scala/tools/scalac/typechecker/Analyzer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,20 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer(
sym.getType().isInstanceOf[Type$PolyType] &&
sym.typeParams().length == 0;

/**
* Returns true if the adaption 'from => to' corresponds to a delegate
* forward view.
*
* TODO: The check should probably be deeper than just for a function type
* because the user might introduce a view from a delegate type to a
* function type himself, which is okay as long as it doesn't collide with
* the forward view.
*/
private def isDelegateForwardView(from: Type, to: Type): boolean =
definitions.DELEGATE_TYPE() != null &&
from.isSubType(definitions.DELEGATE_TYPE()) &&
to.isFunctionType();

// Views -----------------------------------------------------------------------

private def applyView(v: View, tree: Tree, mode: int, pt: Type): Tree = {
Expand Down Expand Up @@ -1646,6 +1660,13 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer(
case _ =>
}
val v = infer.bestView(tree.getType(), pt, Names.EMPTY);
// Convert views of delegate types to closures wrapped around
// the expression's apply method.
if(global.target == scalac_Global.TARGET_MSIL &&
v != null && isDelegateForwardView(tree.getType(), pt)) {
val meth: Symbol = tree.symbol().lookup(Names.apply);
return adapt(gen.Select(tree, meth), mode, pt);
}
if (v != null) return applyView(v, tree, mode, pt);
// todo: remove
val coerceMeth: Symbol = tree.getType().lookup(Names.coerce);
Expand Down Expand Up @@ -1814,6 +1835,12 @@ class Analyzer(global: scalac_Global, descr: AnalyzerPhase) extends Transformer(
}
}
}
// MSIL: Forbid chaining of non-variable delegate objects using += and -=
if(global.target == scalac_Global.TARGET_MSIL &&
qual.getType().isSubType(definitions.DELEGATE_TYPE()) &&
(sym.name == Names.PLUSEQ || sym.name == Names.MINUSEQ) &&
!qual.symbol().isVariable())
error(tree.pos, "illegal modification of non-variable delegate");
val qualtype =
if (qual.isInstanceOf[Tree.Super]) context.enclClass.owner.thisType()
else qual.getType();
Expand Down
11 changes: 10 additions & 1 deletion sources/scala/tools/scalac/typechecker/Infer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,16 @@ class Infer(global: scalac_Global, gen: TreeGen, make: TreeFactory) extends scal
if (best != null) {
viewMeths = availableViews(tp);
while (!viewMeths.isEmpty) {
if (viewMeths.head != best &&
// Ugly hack necessary for MSIL delegate support
// Used to be just:
// if (viewMeths.head != best &&
// isApplicable(viewMeths.head.symtype, argtypes, pt, name, false) &&
// !(specializesView(best.symtype, viewMeths.head.symtype) &&
// !specializesView(viewMeths.head.symtype, best.symtype))) {
if ((if (global.target == scalac_Global.TARGET_MSIL)
viewMeths.head.sym != best.sym
else
viewMeths.head != best) &&
isApplicable(viewMeths.head.symtype, argtypes, pt, name, false) &&
!(specializesView(best.symtype, viewMeths.head.symtype) &&
!specializesView(viewMeths.head.symtype, best.symtype))) {
Expand Down
73 changes: 70 additions & 3 deletions sources/scalac/backend/msil/GenMSIL.java
Original file line number Diff line number Diff line change
Expand Up @@ -850,10 +850,50 @@ case Ident(_):
genLoad(args[0], convTo);
return items.StackItem(convTo);
}
// Generate delegate's reverse view
if (sym.name == Names.view &&
CLRTypes.instance().isDelegateType(resType.toType()))
{
assert args.length == 1 : Debug.show(sym);
return createDelegateFromFunction(args[0], resType);
}

return check(invokeMethod(sym, args, resType, true));

case Select(Tree qualifier, _):
// Treat delegate chaining methods += and -= in a special way
if(CLRTypes.instance().isDelegateType(tc.getType(qualifier.type())) &&
(sym.name == Names.PLUSEQ || sym.name == Names.MINUSEQ)) {
Type delegate = tc.getType(qualifier.type());
MSILType delegateType = msilType(delegate);
MethodBase chainer = tc.getMethod(sym);

switch(qualifier) {
case Apply(Tree f, _):
Symbol setterSym = f.symbol().owner().lookup(
Name.fromString(f.symbol().name.toString() +
Names._EQ));
assert !setterSym.isNone() : "Setter method not found";
MethodInfo setter = (MethodInfo)tc.getMethod(setterSym);

// Generate object and argument for the setter call
emitThis(); // FIXME: doesn't work if the variable is
// is a member of another class
genLoad(qualifier, delegateType);
invokeMethod(sym, args, delegateType, false);
code.Emit(OpCodes.Castclass, delegate);
code.Emit(OpCodes.Callvirt, setter);
return items.VoidItem();

case Ident(_):
// Prepare the left-hand side for the store
Item var = gen(qualifier, delegateType);
load(var);
invokeMethod(sym, args, delegateType, false);
code.Emit(OpCodes.Castclass, delegate);
return check(store(var));
}
}
// scala.Any.==
if (sym == defs.ANY_EQEQ) {
return genEq(qualifier, args[0]);
Expand Down Expand Up @@ -1328,12 +1368,39 @@ private Item invokeMethod(Symbol fun, Tree[] args, MSILType resType,
(MethodInfo)method);
res = returnsVoid(method) ? items.VoidItem() : items.StackItem(resType);
}
if (returnsVoid(fun) && !returnsVoid(method)) {
res = drop(res);
}
CLRTypes clrt = CLRTypes.instance();
if (returnsVoid(fun) && !returnsVoid(method)
&& method != clrt.DELEGATE_COMBINE
&& method != clrt.DELEGATE_REMOVE)
{
res = drop(res);
}
return res;
}

private Item createDelegateFromFunction(Tree fun, MSILType type) {
Item ifun = genLoad(fun, msilType(fun.type));
// Get the apply function of the delegate object
Symbol funSym = tc.getSymbol(ifun.type.toType());
Symbol applySym = funSym.lookup(Names.apply);
MethodInfo apply = (MethodInfo) tc.getDelegateApplyMethod(applySym);

// Get the (object, native int) constructor of the delegate type
Type delegateType = type.toType();
ConstructorInfo delegCtor = delegateType.GetConstructor(
new Type[]{tc.OBJECT, Type.GetType("System.IntPtr")});

// Duplicate the object on the stack; we need its apply method
// and Ldvirtftn uses one element.
code.Emit(OpCodes.Dup);
code.Emit(OpCodes.Ldvirtftn, apply);

// Create a new delegate; this uses up the element duplicated above.
code.Emit(OpCodes.Newobj, delegCtor);

return items.StackItem(type);
}

/*
* Returns the MSILType that corresponds to the given scala.symtab.Type
*/
Expand Down
80 changes: 80 additions & 0 deletions sources/scalac/backend/msil/TypeCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,10 @@ private Type getTypeFromKind(int kind) {
}
}

public Symbol getSymbol(Type type) {
return (Symbol) types2symbols.get(type);
}

public Type createType(Symbol clazz) {
try { return createType0(clazz); }
catch (Error e) {
Expand Down Expand Up @@ -963,6 +967,82 @@ case MethodType(Symbol[] vparams, scalac.symtab.Type result):
return method;
}

/**
* Returns all MethodBase objects corresponding to the symbol.
*/
private MethodBase[] getMethods(Symbol sym) {
MethodBase method = (MethodBase) symbols2methods.get(sym);
if (method != null)
return new MethodBase[]{method};
MemberInfo m = ti.getMember(sym);
if (m != null && m instanceof MethodBase) {
method = (MethodBase) m;
} else {
// force the creation of the declaring type
Type owner = getType(sym.owner());
assert owner != null : Debug.show(sym);
method = (MethodBase) symbols2methods.get(sym);
if (method != null)
return new MethodBase[]{method};
switch (sym.info()) {
case MethodType(Symbol[] vparams, scalac.symtab.Type result):
Type[] params = new Type[vparams.length];
Type resType = getType(result);
for (int i = 0; i < params.length; i++)
params[i] = getType(vparams[i]);
if (sym.isInitializer()) {
// The owner of a constructor is the outer class
// so get the result type of the constructor
method = owner.GetConstructor(params);
assert method != null : "cannot find " + owner
+ "::.ctor" + methodSignature(params);
} else {
method = owner instanceof TypeBuilder
? findMethod(sym.owner(), sym)
: owner.GetMethod
(getMethodName(sym.name, params), params, resType);
}
break;
case OverloadedType(Symbol[] alts, scalac.symtab.Type[] alttypes):
MethodBase[] methods = new MethodBase[alts.length];
for (int i = 0; i < alts.length; i++)
methods[i] = getMethod(alts[i]);
return methods;

default:
throw Debug.abort("Symbol doesn't have a method type", sym);
}
assert method != null
: Debug.show(owner) + " => Cannot find method: " + methodSignature(sym);
}
symbols2methods.put(sym, method);
return new MethodBase[]{method};
}

/**
* Returns the exactest apply method.
* The exactest method is taken to be the first method that doesn't have
* an 'object' return type and all 'object' parameter types.
*/
public MethodBase getDelegateApplyMethod(Symbol sym) {
MethodBase methods[] = getMethods(sym);
if(methods.length > 1) {
assert methods.length == 2;
for (int i = 0; i < methods.length; i++) {
if(((MethodInfo)methods[i]).ReturnType != OBJECT)
return methods[i];
ParameterInfo pi[] = methods[i].GetParameters();
for (int j = 0; j < pi.length; j++) {
if(pi[j].ParameterType != OBJECT)
return methods[i];
}
}
throw Debug.abort("Expected apply method not found", sym);
} else {
return methods[0];
}
}

private String getMethodName(Name name, Type[] params) {
if (name == Names.finalize && params.length == 0)
return "Finalize";
Expand Down
8 changes: 8 additions & 0 deletions sources/scalac/symtab/Definitions.java
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,12 @@ public Symbol JAVAREFARRAYTYPE_JAVAREFARRAYTYPE() {
return JAVAREFARRAYTYPE_JAVAREFARRAYTYPE;
}

// MSIL delegate types
public final Symbol DELEGATE_CLASS;
public final Type DELEGATE_TYPE() {
return DELEGATE_CLASS != null ? DELEGATE_CLASS.staticType() : null;
}

//########################################################################
// Public Fields - Global values

Expand Down Expand Up @@ -794,6 +800,8 @@ public Definitions(Global global) {
STRING_CLASS = getClass(forMSIL ? "System.String" : "java.lang.String");
THROWABLE_CLASS =
getClass(forMSIL ? "System.Exception" : "java.lang.Throwable");
// .NET delegate class
DELEGATE_CLASS = forMSIL ? getClass("System.MulticastDelegate") : null;

// the scala value classes
UNIT_CLASS = getClass("scala.Unit");
Expand Down
Loading

0 comments on commit a481860

Please sign in to comment.