//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 10 Jul 09 Andy Frank Creation
//
using compiler
**
** JsPod
**
class JsPod : JsNode
{
new make(JsCompilerSupport s, PodDef pod, TypeDef[] defs) : super(s)
{
this.name = pod.name
this.meta = pod.meta
this.types = JsType[,]
this.props = JsProps[,]
// build native map
this.natives = Str:File[:]
s.compiler.jsFiles?.each |f| { natives[f.name] = f }
defs.each |TypeDef def|
{
// we inline closures directly, so no need to generate
// anonymous types like we do in Java and .NET
if (def.isClosure) return
// TODO FIXIT: do we still need this?
if (def.qname.contains("\$Cvars"))
{
echo("WARN: Cvar class: $def.qname")
return
}
// check for @js facet
if (def.hasFacet("sys::Js") || support.forceJs) types.add(JsType(s,def))
}
// resource files
baseDir := s.compiler.input.baseDir
if (baseDir != null)
{
s.compiler.jsPropsFiles?.each |file|
{
if (file.ext != "props") return
uri := file.uri.relTo(baseDir.uri)
props.add(JsProps(pod, file, uri, s))
}
}
}
override Void write(JsWriter out)
{
// define namespace
writeNs(out, this.name)
// write types
types.each |t|
{
if (t.isNative) writePeer(out, t, false)
else
{
t.write(out)
if (t.hasNatives) writePeer(out, t, true)
}
}
// write type info
writeTypeInfo(out)
// write props resource files
props.each |p| { p.write(out) }
// write closure fields (must be after writeTypeInfo)
writeClosureFields(out)
// write static init
types.each |t| { t.writeStatic(out) }
// write remaining natives
natives.each |f|
{
in := f.in
out.minify(in)
in.close
}
out.w("}).call(this);").nl
}
Void writeNs(JsWriter out, Str name)
{
ns := "(function () {
${requireModules}
if (typeof exports !== 'undefined') {
//fan.$name = exports;
module.exports = fan;
fan.$name = {};
} else {
fan.$name = root.fan.$name = {};
}
"
ns.splitLines.each { out.w(it).nl }
}
Str requireModules()
{
sb := StrBuf()
sb.add("var root=this;
var fan=root.fan;
if (!fan && (typeof require !== 'undefined'))
{
")
support.compiler.pod.depends.each |depend| {
if (depend.name == "sys")
{
sb.add("try { fan = require('fan.js'); }\n")
sb.add("catch (err) { fan = require('sys.js'); }\n")
// sb.add("fan = require('fan.js');\n")
}
// else sb.add("require('${depend.name}.js');\n")
else
{
sb.add("try { require('${depend.name}.js'); } catch (e) { /*ignore*/ }\n")
}
}
return sb.add("}\n").toStr
}
static const Str requireSys :=
"var root=this;
var fan=root.fan;
if (!fan && (typeof require !== 'undefined')) fan = require('sys.js');
"
Void writePeer(JsWriter out, JsType t, Bool isPeer)
{
key := isPeer ? "${t.peer.name}Peer.js" : "${t.name}.js"
file := natives[key]
if (file == null)
{
support.err("Missing native impl for $t.sig", Loc("${t.name}.fan"))
}
else
{
in := file.in
out.minify(in)
in.close
natives.remove(key)
}
}
Void writeTypeInfo(JsWriter out)
{
out.w("fan.${name}.\$pod = fan.sys.Pod.\$add('$name');").nl
out.w("with (fan.${name}.\$pod)").nl
out.w("{").nl
// filter out synthetic types from reflection
reflect := types.findAll |t| { !t.isSynthetic }
// write all types first
reflect.each |t|
{
adder := t.isMixin ? "\$am" : "\$at"
base := "$t.base.pod::$t.base.name"
mixins := t.mixins.join(",") |m| { "'$m.pod::$m.name'" }
facets := t.facets.join(",") |f| { "'$f.type.sig':$f.val.toCode" }
flags := t->flags
out.w(" fan.${t.pod}.${t.name}.\$type = $adder('$t.name','$base',[$mixins],{$facets},$flags);").nl
}
// then write slot info
reflect.each |t|
{
if (t.fields.isEmpty && t.methods.isEmpty) return
//out.w(" \$$i")
out.w(" fan.${t.pod}.${t.name}.\$type")
t.fields.each |f|
{
// don't write for FFI
if (f.ftype.isForeign) return
facets := f.facets.join(",") |x| { "'$x.type.sig':$x.val.toCode" }
out.w(".\$af('$f.origName',$f.flags,'$f.ftype.sig',{$facets})")
}
t.methods.each |m|
{
if (m.isFieldAccessor) return
if (m.params.any |p| { p.paramType.isForeign}) return
params := m.params.join(",") |p| { "new fan.sys.Param('$p.reflectName','$p.paramType.sig',$p.hasDef)" }
facets := m.facets.join(",") |f| { "'$f.type.sig':$f.val.toCode" }
out.w(".\$am('$m.origName',$m.flags,'$m.ret.sig',fan.sys.List.make(fan.sys.Param.\$type,[$params]),{$facets})")
}
out.w(";").nl
}
// pod meta
out.indent
out.w("m_meta = fan.sys.Map.make(fan.sys.Str.\$type, fan.sys.Str.\$type);").nl
meta.each |v, k|
{
out.w("m_meta.set($k.toCode, $v.toCode);").nl
if (k == "pod.version") out.w("m_version = fan.sys.Version.fromStr($v.toCode);").nl
}
out.w("m_meta = m_meta.toImmutable();").nl
out.unindent
// end with block
out.w("}").nl
}
** Write closure function spec fields
Void writeClosureFields(JsWriter out)
{
support.podClosures.write(out)
}
Str name // pod name
Str:Str meta // pod meta
JsType[] types // types in this pod
Str:File natives // natives
JsProps[] props // prop files in this pod
}