//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 2 Dec 05 Brian Frank Creation
// 30 Sep 06 Brian Frank Ported from Java to Fan
//
**
** Normalize the abstract syntax tree:
** - Collapse multiple static new blocks
** - Init static fields in static new block
** - Init instance fields in instance new block
** - Add implicit return in methods
** - Add implicit super constructor call
** - Rewrite synthetic getter/setter for override of concrete field
** - Generate once method boiler plate
**
class Normalize : CompilerStep
{
//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////
new make(Compiler compiler)
: super(compiler)
{
}
//////////////////////////////////////////////////////////////////////////
// Run
//////////////////////////////////////////////////////////////////////////
override Void run()
{
log.debug("Normalize")
walk(types, VisitDepth.typeDef)
bombIfErr
}
//////////////////////////////////////////////////////////////////////////
// Type Normalization
//////////////////////////////////////////////////////////////////////////
override Void visitTypeDef(TypeDef t)
{
location := t.location
iInit := Block.make(location) // instance init
sInit := Block.make(location) // static init
// walk thru all the slots
t.slotDefs.each |SlotDef s|
{
if (s is FieldDef)
{
f := (FieldDef)s
normalizeField(f)
if (f.init != null && !f.isAbstract)
{
if (f.isStatic)
sInit.add(fieldInitStmt(f))
else
iInit.add(fieldInitStmt(f))
f.init = null // don't walk the init expr anymore
}
}
else
{
// if a static initializer, append it
m := (MethodDef)s
if (m.isStaticInit)
appendStaticInit(sInit, m)
else
normalizeMethod(m)
}
}
// add instance$init if needed
if (!iInit.isEmpty)
{
iInit.add(ReturnStmt.make(location))
t.addSlot(MethodDef.makeInstanceInit(iInit.location, t, iInit))
}
// add static$init if needed
if (!sInit.isEmpty)
{
sInit.add(ReturnStmt.make(location))
t.normalizeStaticInits(MethodDef.makeStaticInit(sInit.location, t, sInit))
}
}
private Void appendStaticInit(Block sInit, MethodDef m)
{
// append inside an "if (true) {}" block so that each static
// initializer is given its own scope in the unified static initializer;
// the "if (true)" gets optimized away in CoodeAsm
loc := m.location
ifStmt := IfStmt.make(loc)
ifStmt.condition = LiteralExpr.make(loc, ExprId.trueLiteral, ns.boolType, true)
ifStmt.trueBlock = m.code
sInit.add(ifStmt)
m.code = null
}
//////////////////////////////////////////////////////////////////////////
// Method Normalization
//////////////////////////////////////////////////////////////////////////
private Void normalizeMethod(MethodDef m)
{
code := m.code
if (code == null) return
// add implicit return
if (!code.isExit) code.add(ReturnStmt.make(code.location))
// insert super constructor call
if (m.isCtor) insertSuperCtor(m)
// once
if (m.isOnce) normalizeOnce(m)
}
private Void insertSuperCtor(MethodDef m)
{
// never insert super call for synthetic types, mixins, or Obj.make
parent := m.parent
base := parent.base
if (parent.isSynthetic) return
if (parent.isMixin) return
if (base.isObj) return
// check if the base class has exactly one available
// constructor with no parameters
superCtors := base.ctors
if (superCtors.size != 1) return
superCtor := superCtors.first
if (superCtor.isPrivate) return
if (superCtor.isInternal && base.pod != parent.pod) return
if (!superCtor.params.isEmpty) return
// if we find a ctor to use, then create an implicit super call
m.ctorChain = CallExpr.makeWithMethod(m.location, SuperExpr.make(m.location), superCtor)
m.ctorChain.isCtorChain = true
}
private Void normalizeOnce(MethodDef m)
{
loc := m.location
// we'll report these errors in CheckErrors
if (curType.isConst || curType.isMixin ||
m.isStatic || m.isCtor || m.isFieldAccessor)
return
// error checking
if (m.ret.isVoid) err("Once method '$m.name' cannot return Void", loc)
if (!m.params.isEmpty) err("Once method '$m.name' cannot have parameters", loc)
// generate storage field
f := FieldDef.make(loc, curType)
f.flags = FConst.Private | FConst.Storage | FConst.Synthetic
f.name = m.name + "\$Store"
f.fieldType = ns.objType
f.init = LiteralExpr.makeFor(loc, ns, "_once_")
curType.addSlot(f)
// generate once implementation method
o := MethodDef.make(loc, curType)
o.flags = FConst.Private | FConst.Synthetic
o.name = m.name + "\$Once"
o.ret = ns.objType
o.code = m.code
curType.addSlot(o)
// rewrite wrapper method to delegate:
// if (name$Store == "_once_")
// name$Store = name$Once()
// return (RetType)name$Store
ifStmt := IfStmt.make(loc)
// if (name$Store == "_once_")
ifStmt.condition = BinaryExpr.make(
f.makeAccessorExpr(loc, false),
Token.same,
LiteralExpr.makeFor(loc, ns, "_once_"))
// name$Store = name$Once()
ifStmt.trueBlock = Block.make(loc)
ifStmt.trueBlock.add(BinaryExpr.make(
f.makeAccessorExpr(loc, false),
Token.assign,
CallExpr.makeWithMethod(loc, ThisExpr.make(loc), o)
).toStmt)
// return (RetType)name$Store
retStmt := ReturnStmt.make(loc)
retStmt.expr = TypeCheckExpr.cast(
f.makeAccessorExpr(loc, false),
m.ret)
m.code = Block.make(loc)
m.code.add(ifStmt)
m.code.add(retStmt)
}
//////////////////////////////////////////////////////////////////////////
// Field Normalization
//////////////////////////////////////////////////////////////////////////
private Void normalizeField(FieldDef f)
{
// if this field overrides a concrete field, that means we already have
// a concrete getter/setter for this field - if either of this field's
// accessors is synthetic, then rewrite the one generated by Parser with
// one that calls the "super" version of the accessor
if (f.concreteBase != null)
{
if (!f.hasGet) genSyntheticOverrideGet(f)
if (!f.hasSet) genSyntheticOverrideSet(f)
}
// ensure that getter is using inherited return
// in case we have a covariant override
if (f.get != null)
f.get.inheritedRet = f.inheritedRet
}
private Void genSyntheticOverrideGet(FieldDef f)
{
loc := f.location
f.get.code.stmts.clear
f.get.code.add(ReturnStmt.make(loc, FieldExpr.make(loc, SuperExpr.make(loc), f.concreteBase)))
}
private Void genSyntheticOverrideSet(FieldDef f)
{
loc := f.location
lhs := FieldExpr.make(loc, SuperExpr.make(loc), f.concreteBase)
rhs := UnknownVarExpr.make(loc, null, "val")
code := f.get.code
f.set.code.stmts.clear
f.set.code.add(BinaryExpr.makeAssign(lhs, rhs).toStmt)
f.set.code.add(ReturnStmt.make(loc))
}
private static ExprStmt fieldInitStmt(FieldDef f)
{
useAccessor := f.concreteBase != null
lhs := f.makeAccessorExpr(f.location, useAccessor)
rhs := f.init
return BinaryExpr.makeAssign(lhs, rhs).toStmt
}
}