//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 9 Jul 09 Andy Frank Creation
//
using compiler
**
** JsExpr
**
abstract class JsExpr : JsNode
{
new make(JsCompilerSupport s, Expr? expr := null) : super(s, expr)
{
}
internal Bool isLocalDefStmt := false
static JsExpr makeFor(JsCompilerSupport s, Expr expr)
{
switch (expr.id)
{
case ExprId.nullLiteral: return JsNullLiteralExpr(s, expr)
case ExprId.trueLiteral: return JsBoolLiteralExpr(s, expr, true)
case ExprId.falseLiteral: return JsBoolLiteralExpr(s, expr, false)
case ExprId.intLiteral: return JsIntLiteralExpr(s, expr)
case ExprId.floatLiteral: return JsFloatLiteralExpr(s, expr)
case ExprId.decimalLiteral: return JsDecimalLiteralExpr(s, expr)
case ExprId.strLiteral: return JsStrLiteralExpr(s, expr)
case ExprId.durationLiteral: return JsDurationLiteralExpr(s, expr)
case ExprId.uriLiteral: return JsUriLiteralExpr(s, expr)
case ExprId.typeLiteral: return JsTypeLiteralExpr(s, expr)
case ExprId.slotLiteral: return JsSlotLiteralExpr(s, expr)
case ExprId.rangeLiteral: return JsRangeLiteralExpr(s, expr)
case ExprId.listLiteral: return JsListLiteralExpr(s, expr)
case ExprId.mapLiteral: return JsMapLiteralExpr(s, expr)
case ExprId.boolNot: return JsUnaryExpr(s, expr)
case ExprId.cmpNull: return JsUnaryExpr(s, expr)
case ExprId.cmpNotNull: return JsUnaryExpr(s, expr)
case ExprId.elvis: return JsElvisExpr(s, expr)
case ExprId.assign: return JsBinaryExpr(s, expr)
case ExprId.same: return JsBinaryExpr(s, expr)
case ExprId.notSame: return JsBinaryExpr(s, expr)
case ExprId.ternary: return JsTernaryExpr(s, expr)
case ExprId.boolOr: return JsCondExpr(s, expr)
case ExprId.boolAnd: return JsCondExpr(s, expr)
case ExprId.isExpr: return JsTypeCheckExpr(s, expr)
case ExprId.isnotExpr: return JsTypeCheckExpr(s, expr)
case ExprId.asExpr: return JsTypeCheckExpr(s, expr)
case ExprId.coerce: return JsTypeCheckExpr(s, expr)
case ExprId.call: return JsCallExpr(s, expr)
case ExprId.construction: return JsCallExpr(s, expr)
case ExprId.shortcut: return JsShortcutExpr(s, expr)
case ExprId.field: return JsFieldExpr(s, expr)
case ExprId.closure: return JsClosureExpr(s, expr)
case ExprId.localVar: return JsLocalVarExpr(s, expr)
case ExprId.thisExpr: return JsThisExpr(s)
case ExprId.superExpr: return JsSuperExpr(s, expr)
case ExprId.itExpr: return JsItExpr(s, expr)
case ExprId.staticTarget: return JsStaticTargetExpr(s, expr)
case ExprId.throwExpr: return JsThrowExpr(s, expr)
// Not implemented
//case ExprId.unknownVar
//case ExprId.storage
//case ExprId.curry
//case ExprId.complexLiteral
default: throw s.err("Unknown ExprId: $expr.id", expr.loc)
}
}
}
**************************************************************************
** JsThisExpr
**************************************************************************
class JsThisExpr : JsExpr
{
new make(JsCompilerSupport s) : super(s) {}
override Void write(JsWriter out) { out.w(support.thisName) }
}
**************************************************************************
** JsSuperExpr
**************************************************************************
class JsSuperExpr : JsExpr
{
new make(JsCompilerSupport s, SuperExpr se) : super(s, se)
{
type = JsTypeRef(s, se.ctype, se.loc)
if (se.explicitType != null)
explicitType = JsTypeRef(s, se.explicitType, se.loc)
}
override Void write(JsWriter out)
{
t := explicitType ?: type
out.w("${t.qname}.prototype", loc)
}
JsTypeRef type
JsTypeRef? explicitType
}
**************************************************************************
** JsItExpr
**************************************************************************
class JsItExpr : JsExpr
{
new make(JsCompilerSupport s, ItExpr itExpr) : super(s, itExpr) { }
override Void write(JsWriter out) { out.w("it", loc) }
}
**************************************************************************
** JsLocalVarExpr
**************************************************************************
class JsLocalVarExpr : JsExpr
{
new make(JsCompilerSupport s, LocalVarExpr le) : super(s, le)
{
name = vnameToJs(le.var.name)
}
override Void write(JsWriter out)
{
out.w(name, loc)
}
Str name
}
**************************************************************************
** JsStaticTargetExpr
**************************************************************************
class JsStaticTargetExpr : JsExpr
{
new make(JsCompilerSupport s, StaticTargetExpr le) : super(s, le)
{
target = JsTypeRef(s, le.ctype, le.loc)
}
override Void write(JsWriter out)
{
out.w(target.qname, loc)
}
JsTypeRef target
}
**************************************************************************
** JsNullLiteralExpr
**************************************************************************
class JsNullLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x) {}
override Void write(JsWriter out) { out.w("null", loc) }
}
**************************************************************************
** JsBoolLiteralExpr
**************************************************************************
class JsBoolLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x, Bool val) : super(s, x)
{
this.val = val
}
override Void write(JsWriter out)
{
out.w(val ? "true" : "false", loc)
}
Bool val
}
**************************************************************************
** JsIntLiteralExpr
**************************************************************************
class JsIntLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = x.val
}
override Void write(JsWriter out)
{
out.w(val, loc)
}
Int val
}
**************************************************************************
** JsFloatLiteralExpr
**************************************************************************
class JsFloatLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = x.val
}
override Void write(JsWriter out)
{
out.w("fan.sys.Float.make($val)", loc)
}
Float val
}
**************************************************************************
** JsDecimalLiteralExpr
**************************************************************************
class JsDecimalLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = x.val
}
override Void write(JsWriter out)
{
out.w("fan.sys.Decimal.make($val)", loc)
}
Decimal val
}
**************************************************************************
** JsStrLiteralExpr
**************************************************************************
class JsStrLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = x.val
this.esc = val.toCode('\"', true)[1..-2] // remove outer quotes
}
override Void write(JsWriter out)
{
out.w("\"$esc\"", loc)
}
Str val
Str esc
}
**************************************************************************
** JsDurationLiteralExpr
**************************************************************************
class JsDurationLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = x.val
}
override Void write(JsWriter out)
{
out.w("fan.sys.Duration.fromStr(\"$val.toStr\")", loc)
}
Duration val
}
**************************************************************************
** JsUriLiteralExpr
**************************************************************************
class JsUriLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = x.val
this.str = val.toStr.toCode('\"', true)
}
override Void write(JsWriter out)
{
out.w("fan.sys.Uri.fromStr(", loc)
out.w(val.toStr.toCode('\"', true), loc)
out.w(")")
}
Obj val
Str str
}
**************************************************************************
** JsTypeLiteralExpr
**************************************************************************
class JsTypeLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, LiteralExpr x) : super(s, x)
{
this.val = JsTypeRef(s, x.val, x.loc)
}
override Void write(JsWriter out)
{
writeType(val, out)
}
static Void writeType(JsTypeRef t, JsWriter out)
{
if (t.isList || t.isMap || t.isFunc)
{
out.w("fan.sys.Type.find(\"$t.sig\")", t.loc)
}
else
{
out.w("${t.qname}.\$type", t.loc)
if (t.isNullable) out.w(".toNullable()", t.loc)
}
}
JsTypeRef val
}
**************************************************************************
** JsSlotLiteralExpr
**************************************************************************
class JsSlotLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, SlotLiteralExpr x) : super(s, x)
{
this.parent = JsTypeRef(s, x.parent, x.loc)
this.name = x.name
}
override Void write(JsWriter out)
{
JsTypeLiteralExpr.writeType(parent, out)
out.w(".slot(\"$name\")", loc)
}
JsTypeRef parent // slot parent type
Str name // slot name
}
**************************************************************************
** JsRangeLiteralExpr
**************************************************************************
class JsRangeLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, RangeLiteralExpr x) : super(s, x)
{
start = JsExpr.makeFor(s, x.start)
end = JsExpr.makeFor(s, x.end)
exclusive = x.exclusive
}
override Void write(JsWriter out)
{
out.w("fan.sys.Range.make(", loc)
start.write(out)
out.w(",")
end.write(out)
if (exclusive) out.w(",true", loc)
out.w(")")
}
JsExpr start
JsExpr end
Bool exclusive
}
**************************************************************************
** JsListLiteralExpr
**************************************************************************
class JsListLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, ListLiteralExpr x) : super(s, x)
{
this.inferredType = JsTypeRef(s, x.ctype, x.loc)
if (x.explicitType != null)
this.explicitType = JsTypeRef(s, x.explicitType, x.loc)
this.vals = x.vals.map |v->JsExpr| { JsExpr.makeFor(s, v) }
}
override Void write(JsWriter out)
{
of := (explicitType ?: inferredType).v
out.w("fan.sys.List.make(", loc)
JsTypeLiteralExpr.writeType(of, out)
if (vals.size > 0)
{
out.w(", [")
vals.each |v,i|
{
if (i > 0) out.w(",")
v.write(out)
}
out.w("]")
}
out.w(")")
}
JsTypeRef inferredType
JsTypeRef? explicitType
JsExpr[] vals
}
**************************************************************************
** JsMapLiteralExpr
**************************************************************************
class JsMapLiteralExpr : JsExpr
{
new make(JsCompilerSupport s, MapLiteralExpr me) : super(s, me)
{
this.inferredType = JsTypeRef(s, me.ctype, me.loc)
if (me.explicitType != null)
this.explicitType = JsTypeRef(s, me.explicitType, me.loc)
this.keys = me.keys.map |k->JsExpr| { JsExpr.makeFor(s, k) }
this.vals = me.vals.map |v->JsExpr| { JsExpr.makeFor(s, v) }
}
override Void write(JsWriter out)
{
out.w("fan.sys.Map.fromLiteral([", loc)
keys.each |k,i| { if (i > 0) out.w(","); k.write(out) }
out.w("],[")
vals.each |v,i| { if (i > 0) out.w(","); v.write(out) }
out.w("]")
t := explicitType ?: inferredType
out.w(",fan.sys.Type.find(\"", loc).w(t.k.sig, loc).w("\")")
out.w(",fan.sys.Type.find(\"", loc).w(t.v.sig, loc).w("\")")
out.w(")")
}
JsTypeRef inferredType
JsTypeRef? explicitType
JsExpr[] keys
JsExpr[] vals
}
**************************************************************************
** JsUnaryExpr
**************************************************************************
class JsUnaryExpr : JsExpr
{
new make(JsCompilerSupport s, UnaryExpr x) : super(s, x)
{
this.id = x.id
this.symbol = x.opToken.symbol
this.operand = JsExpr.makeFor(s, x.operand)
}
override Void write(JsWriter out)
{
switch (id)
{
case ExprId.cmpNull: operand.write(out); out.w(" == null", loc)
case ExprId.cmpNotNull: operand.write(out); out.w(" != null", loc)
default:
out.w(symbol, loc)
if (operand is JsBinaryExpr) out.w("(")
operand.write(out)
if (operand is JsBinaryExpr) out.w(")")
}
}
ExprId id
Str symbol
JsExpr operand
}
**************************************************************************
** JsBinaryExpr
**************************************************************************
class JsBinaryExpr : JsExpr
{
new make(JsCompilerSupport s, BinaryExpr x) : super(s, x)
{
this.symbol = x.opToken.symbol
this.lhs = JsExpr.makeFor(s, x.lhs)
this.rhs = JsExpr.makeFor(s, x.rhs)
this.leave = x.leave
this.isAssign = x.assignTarget != null
this.x = x
}
override Void write(JsWriter out)
{
if (isAssign && lhs is JsFieldExpr)
{
fe := (JsFieldExpr)lhs
if (leave)
{
var := support.unique
old := support.thisName
support.thisName = "\$this"
out.w("(function(\$this) {", loc)
out.w(" var $var = ", loc); rhs.write(out); out.w("; ")
fe.writeSetter(out, JsVarExpr(support, var)); out.w(";")
out.w(" return $var;", loc)
out.w(" })($old)", loc)
support.thisName = old
}
else { fe.writeSetter(out, rhs) }
}
else
{
if (isAssign && !isLocalDefStmt) out.w("(")
lhs.write(out)
out.w(" $symbol ", loc)
rhs.write(out)
if (isAssign && !isLocalDefStmt) out.w(")")
}
}
Str symbol
JsExpr lhs
JsExpr rhs
Bool leave
Bool isAssign
BinaryExpr x
}
// for JsBinaryExpr support
internal class JsVarExpr : JsExpr
{
new make(JsCompilerSupport s, Str name) : super(s) { this.name = name }
override Void write(JsWriter out) { out.w(name) }
Str name
}
**************************************************************************
** JsTernaryExpr
**************************************************************************
class JsTernaryExpr : JsExpr
{
new make(JsCompilerSupport s, TernaryExpr te) : super(s, te)
{
this.condition = JsExpr.makeFor(s, te.condition)
this.trueExpr = JsExpr.makeFor(s, te.trueExpr)
this.falseExpr = JsExpr.makeFor(s, te.falseExpr)
}
override Void write(JsWriter out)
{
var := support.unique
old := support.thisName
support.thisName = "\$this"
out.w("(function(\$this) { ", loc)
out.w("if ("); condition.write(out); out.w(") ")
if (trueExpr isnot JsThrowExpr) out.w("return ", loc)
trueExpr.write(out); out.w("; ")
if (falseExpr isnot JsThrowExpr) out.w("return ", loc)
falseExpr.write(out); out.w("; ")
out.w("})($old)", loc)
support.thisName = old
}
JsExpr condition
JsExpr trueExpr
JsExpr falseExpr
}
**************************************************************************
** JsElvisExpr
**************************************************************************
class JsElvisExpr : JsExpr
{
new make(JsCompilerSupport s, BinaryExpr be) : super(s, be)
{
this.lhs = JsExpr.makeFor(s, be.lhs)
this.rhs = JsExpr.makeFor(s, be.rhs)
}
override Void write(JsWriter out)
{
var := support.unique
old := support.thisName
support.thisName = "\$this"
out.w("(function(\$this) { var $var = ", loc)
lhs.write(out)
out.w("; if ($var != null) return $var; ", loc)
if (rhs isnot JsThrowExpr) out.w("return ", loc)
rhs.write(out)
out.w("; })($old)", loc)
support.thisName = old
}
JsExpr lhs
JsExpr rhs
}
**************************************************************************
** JsCondExpr
**************************************************************************
class JsCondExpr : JsExpr
{
new make(JsCompilerSupport s, CondExpr ce) : super(s, ce)
{
this.symbol = ce.opToken.symbol
this.operands = ce.operands.map |op->JsExpr| { JsExpr.makeFor(s, op) }
}
override Void write(JsWriter out)
{
out.w("(")
operands.each |op,i|
{
if (i>0 && i<operands.size) out.w(" $symbol ", loc)
op.write(out)
}
out.w(")")
}
Str symbol
JsExpr[] operands
}
**************************************************************************
** JsTypeCheckExpr
**************************************************************************
class JsTypeCheckExpr : JsExpr
{
new make(JsCompilerSupport s, TypeCheckExpr te) : super(s, te)
{
this.op = te.id == ExprId.coerce ? "coerce" : te.opStr
this.target = JsExpr.makeFor(s, te.target)
this.check = JsTypeRef(s, te.check, te.loc)
}
override Void write(JsWriter out)
{
m := op
if (m == "isnot")
{
out.w("!", loc)
m = "is"
}
out.w("fan.sys.ObjUtil.$m(", loc)
target.write(out)
out.w(",")
JsTypeLiteralExpr.writeType(check, out)
out.w(")")
}
Str op
JsExpr target
JsTypeRef check
}
**************************************************************************
** JsCallExpr
**************************************************************************
class JsCallExpr : JsExpr
{
new make(JsCompilerSupport s, CallExpr ce) : super(s, ce)
{
this.method = JsMethodRef(s, ce.method)
this.name = method.name
this.args = ce.args.map |a->JsExpr| { JsExpr.makeFor(s, a) }
this.isSafe = ce.isSafe
this.isMock = ce.method is MockMethod
this.isCtorChain = ce.isCtorChain
this.isDynamic = ce.isDynamic
if (isDynamic) this.dynamicName = ce.name
if (ce.method != null)
{
this.parent = JsTypeRef(s, ce.method.parent, ce.loc)
this.isCtor = ce.method.isCtor
this.isObj = ce.method.parent.qname == "sys::Obj"
this.isPrim = isPrimitive(ce.method.parent)
this.isStatic = ce.method.isStatic
}
if (ce.target != null)
{
this.target = JsExpr.makeFor(s, ce.target)
this.targetType = ce.target.ctype == null ? parent : JsTypeRef(s, ce.target.ctype, ce.loc)
}
// force these methods to route thru ObjUtil if not a super.xxx expr
if ((name == "equals" || name == "compare") && (target isnot JsSuperExpr)) isObj = true
// use isMock as hook to skip instance inits
if (name.startsWith("instance\$init\$")) isMock = true
}
override Void write(JsWriter out)
{
// skip mock methods used to insert implicit runtime checks
if (isMock) return
if (isSafe)
{
// wrap if safe-nav
safeVar = support.unique
old := support.thisName
support.thisName = "\$this"
out.w("(function(\$this) { var $safeVar = ", loc)
if (target == null) out.w(support.thisName, loc)
else target.write(out)
out.w("; if ($safeVar == null) return null; return ", loc)
writeCall(out)
out.w("; })($old)", loc)
support.thisName = old
}
else
{
// normal call
writeCall(out)
}
}
Void writeCall(JsWriter out)
{
if (isObj) writeObj(out)
else if (isPrim) writePrimitive(out)
else if (isCtorChain) writeCtorChain(out)
else if (target is JsSuperExpr) writeSuper(out)
else
{
writeTarget(out)
out.w(".$name(", loc)
writeArgs(out)
out.w(")")
}
}
Void writeObj(JsWriter out)
{
out.w("fan.sys.ObjUtil.$name(", loc)
if (isStatic) writeArgs(out)
else
{
writeTarget(out)
writeArgs(out, true)
}
out.w(")")
}
Void writePrimitive(JsWriter out)
{
out.w("${targetType.qname}.$name(", loc)
if (isStatic) writeArgs(out)
else
{
writeTarget(out)
writeArgs(out, true)
}
out.w(")")
}
Void writeCtorChain(JsWriter out)
{
out.w("${targetType.qname}.${name}\$($support.thisName", loc)
writeArgs(out, true)
out.w(")")
}
Void writeSuper(JsWriter out)
{
target.write(out)
out.w(".${name}.call($support.thisName", loc)
writeArgs(out, true)
out.w(")")
}
Void writeTarget(JsWriter out)
{
if (isStatic || isCtor) parent.write(out)
else if (safeVar != null) out.w(safeVar)
else if (target == null) out.w(support.thisName)
else target.write(out)
}
Void writeArgs(JsWriter out, Bool hasFirstArg := false)
{
if (isDynamic)
{
if (hasFirstArg) out.w(",")
out.w("\"$dynamicName\",fan.sys.List.make(fan.sys.Obj.\$type.toNullable(),[", loc)
hasFirstArg = false
}
args.each |arg,i|
{
if (hasFirstArg || i > 0) out.w(",")
arg.write(out)
}
if (isDynamic) out.w("])")
}
JsMethodRef method // method ref
JsExpr? target // call target
JsTypeRef? targetType // call target type
JsTypeRef? parent // method parent type
Str name // method name
JsExpr[] args // args to pass to method
Bool isObj // is target sys::Obj
Bool isPrim // is target a primitive type (Int,Bool,etc)
Bool isSafe // if ?. operator
Str? safeVar // var target expr is held in for safe-nav
Bool isMock // mock methods used to insert implicit runtime checks
Bool isCtor // is this a ctor call
Bool isCtorChain // is this a ctor chain call
Bool isStatic // is this a static method
Bool isDynamic // is this a -> call
Str? dynamicName // name of -> call
}
**************************************************************************
** JsShortcutExpr
**************************************************************************
class JsShortcutExpr : JsCallExpr
{
new make(JsCompilerSupport s, ShortcutExpr se) : super(s, se)
{
this.symbol = se.opToken.symbol
this.isAssign = se.isAssign
this.isIndexedAssign = se is IndexedAssignExpr
this.isPostfixLeave = se.isPostfixLeave
this.leave = se.leave
switch (symbol)
{
case "!=": name = "compareNE"
case "<": name = "compareLT"
case "<=": name = "compareLE"
case ">=": name = "compareGE"
case ">": name = "compareGT"
}
if (isAssign) assignTarget = findAssignTarget(target, se.loc)
if (isIndexedAssign) assignIndex = findIndexedAssign(target, se.loc).args[0]
}
private JsExpr findAssignTarget(JsExpr expr, Loc loc)
{
if (expr is JsLocalVarExpr || expr is JsFieldExpr) return expr
t := Type.of(expr).field("target", false)
if (t != null) return findAssignTarget(t.get(expr), loc)
throw support.err("No base Expr found", loc)
}
private JsShortcutExpr findIndexedAssign(JsExpr expr, Loc loc)
{
if (expr is JsShortcutExpr) return expr
t := Type.of(expr).field("target", false)
if (t != null) return findIndexedAssign(t.get(expr), loc)
throw support.err("No base Expr found", loc)
}
override Void write(JsWriter out)
{
if (fieldSet)
{
super.write(out)
return
}
if (isIndexedAssign)
{
doWriteIndexedAssign(out)
return
}
if (isPostfixLeave)
{
var := support.unique
old := support.thisName
support.thisName = "\$this"
out.w("(function(\$this) { var $var = ", loc)
assignTarget.write(out)
out.w("; ")
doWrite(out)
out.w("; return $var; })($old)", loc)
support.thisName = old
}
else doWrite(out)
}
Void doWrite(JsWriter out)
{
if (isAssign)
{
if (assignTarget is JsFieldExpr)
{
fieldSet = true
fe := (JsFieldExpr)assignTarget
fe.writeSetter(out, this)
fieldSet = false
return
}
else
{
assignTarget.write(out)
out.w(" = ", loc)
}
}
super.write(out)
}
Void doWriteIndexedAssign(JsWriter out)
{
newVal := support.unique
oldVal := support.unique
ref := support.unique
index := support.unique
old := support.thisName
retVal := isPostfixLeave ? oldVal : newVal
support.thisName = "\$this"
out.w("(function(\$this) {", loc)
out.w(" var $ref = ", loc); assignTarget.write(out); out.w(";")
out.w(" var $index = ", loc); assignIndex.write(out); out.w(";")
out.w(" var $newVal = ", loc ); super.write(out); out.w(";")
if (isPostfixLeave) out.w(" var $oldVal = ${ref}.get($index);", loc);
out.w(" ${ref}.set($index,$newVal);", loc)
out.w(" return ${retVal};", loc)
out.w(" })($old)", loc)
support.thisName = old
}
Str symbol // the shortcut token symbol
Bool isAssign // does this expr assign
Bool isIndexedAssign // is indexed assign
Bool isPostfixLeave // is postfix expr
JsExpr? assignTarget // target of assignment
JsExpr? assignIndex // indexed assign: index
Bool leave // leave result of expr on "stack"
Bool fieldSet := false // transietly used for field sets
}
**************************************************************************
** JsFieldExpr
**************************************************************************
class JsFieldExpr : JsExpr
{
new make(JsCompilerSupport s, FieldExpr fe) : super(s, fe)
{
if (fe.target != null) this.target = JsExpr.makeFor(s, fe.target)
this.parent = JsTypeRef(s, fe.field.parent, fe.loc)
this.field = JsFieldRef(s, fe.field)
this.isSafe = fe.isSafe
this.useAccessor = fe.useAccessor
}
Void writeSetter(JsWriter out, JsExpr arg)
{
this.setArg = arg
this.write(out)
this.setArg = null
}
override Void write(JsWriter out)
{
if (target is JsSuperExpr) writeSuper(out)
else writeNorm(out)
}
private Void writeSuper(JsWriter out)
{
name := field.name
if (setArg != null) name += "\$"
target.write(out)
out.w(".${name}.call($support.thisName", loc)
if (setArg != null)
{
out.w(",")
writeSetArg(out)
}
out.w(")")
}
private Void writeNorm(JsWriter out)
{
old := support.thisName
if (isSafe)
{
v := support.unique
support.thisName = "\$this"
out.w("(function(\$this) { var $v=", loc)
writeTarget(out)
out.w("; return ($v==null) ? null : $v", loc)
support.thisName = old
}
else
{
writeTarget(out)
if (field.name == "\$this") return // skip $this ref for closures
}
out.w(".")
if (useAccessor)
{
out.w("$field.name", loc)
if (setArg == null) out.w("()")
else
{
out.w("\$(")
writeSetArg(out)
out.w(")")
}
}
else
{
out.w("m_$field.name", loc)
if (setArg != null)
{
out.w(" = ")
writeSetArg(out)
}
}
if (isSafe) out.w(" }($old))", loc)
}
private Void writeTarget(JsWriter out)
{
if (target == null) parent.write(out)
else target.write(out)
}
private Void writeSetArg(JsWriter out)
{
arg := setArg
this.setArg = null
arg.write(out)
this.setArg = arg
}
JsExpr? target // field target
JsTypeRef parent // field parent type
JsFieldRef field // field
Bool useAccessor // false if access using '&' storage operator
Bool isSafe // is safe nav
JsExpr? setArg // transiently use for setters
}
**************************************************************************
** JsClosureExpr
**************************************************************************
class JsClosureExpr : JsExpr
{
new make(JsCompilerSupport s, ClosureExpr ce) : super(s, ce)
{
this.ce = ce
}
override Void write(JsWriter out)
{
support.podClosures.writeClosure(ce, out)
}
ClosureExpr ce
}
**************************************************************************
** JsThrowExpr
**************************************************************************
class JsThrowExpr : JsExpr
{
new make(JsCompilerSupport s, ThrowExpr te) : super(s, te)
{
this.exception = JsExpr.makeFor(s, te.exception)
}
override Void write(JsWriter out)
{
out.w("throw ", loc)
exception.write(out)
}
JsExpr exception // the exception to throw
}