js: Add support for sys::List parameterized type reflection
authorAndy Frank <afrankvt@gmail.com>
Wed Feb 15 10:38:21 2012 -0500 (15 months ago)
changeset 3633d07881ac45e1
parent 3632 b610b3e5b900
child 3634 4577c8b36090
js: Add support for sys::List parameterized type reflection
src/sys/js/fan/Method.js
src/sys/js/fan/Param.js
src/sys/js/fan/Type.js
src/sys/js/fanx/TypeParser.js
     1.1 --- a/src/sys/js/fan/Method.js	Wed Feb 15 08:55:43 2012 -0500
     1.2 +++ b/src/sys/js/fan/Method.js	Wed Feb 15 10:38:21 2012 -0500
     1.3 @@ -16,8 +16,10 @@
     1.4  // Constructor
     1.5  //////////////////////////////////////////////////////////////////////////
     1.6  
     1.7 -fan.sys.Method.prototype.$ctor = function(parent, name, flags, returns, params, facets)
     1.8 +fan.sys.Method.prototype.$ctor = function(parent, name, flags, returns, params, facets, generic)
     1.9  {
    1.10 +  if (generic === undefined) generic = null;
    1.11 +
    1.12    this.m_parent  = parent;
    1.13    this.m_name    = name;
    1.14    this.m_qname   = parent.qname() + "." + name;
    1.15 @@ -28,6 +30,23 @@
    1.16    this.m_$name   = this.$$name(name);
    1.17    this.m_$qname  = this.m_parent.m_$qname + '.' + this.m_$name;
    1.18    this.m_facets  = new fan.sys.Facets(facets);
    1.19 +  this.m_mask    = (generic != null) ? 0 : fan.sys.Method.toMask(parent, returns, params);
    1.20 +  this.m_generic = generic;
    1.21 +}
    1.22 +
    1.23 +fan.sys.Method.GENERIC = 0x01;
    1.24 +fan.sys.Method.toMask = function(parent, returns, params)
    1.25 +{
    1.26 +  // we only use generics in Sys
    1.27 +  if (parent.pod().$name() != "sys") return 0;
    1.28 +
    1.29 +  var p = returns.isGenericParameter() ? 1 : 0;
    1.30 +  for (var i=0; i<params.size(); ++i)
    1.31 +    p |= params.get(i).m_type.isGenericParameter() ? 1 : 0;
    1.32 +
    1.33 +  var mask = 0;
    1.34 +  if (p != 0) mask |= fan.sys.Method.GENERIC;
    1.35 +  return mask;
    1.36  }
    1.37  
    1.38  //////////////////////////////////////////////////////////////////////////
    1.39 @@ -61,6 +80,14 @@
    1.40  fan.sys.Method.prototype.func = function() { return this.m_func; }
    1.41  
    1.42  //////////////////////////////////////////////////////////////////////////
    1.43 +// Generics
    1.44 +//////////////////////////////////////////////////////////////////////////
    1.45 +
    1.46 +fan.sys.Method.prototype.isGenericMethod = function() { return (this.m_mask & fan.sys.Method.GENERIC) != 0; }
    1.47 +fan.sys.Method.prototype.isGenericInstance = function() { return this.m_generic != null; }
    1.48 +fan.sys.Method.prototype.getGenericMethod = function() { return this.m_generic; }
    1.49 +
    1.50 +//////////////////////////////////////////////////////////////////////////
    1.51  // Call Conveniences
    1.52  //////////////////////////////////////////////////////////////////////////
    1.53  
     2.1 --- a/src/sys/js/fan/Param.js	Wed Feb 15 08:55:43 2012 -0500
     2.2 +++ b/src/sys/js/fan/Param.js	Wed Feb 15 10:38:21 2012 -0500
     2.3 @@ -19,7 +19,7 @@
     2.4  fan.sys.Param.prototype.$ctor = function(name, type, hasDefault)
     2.5  {
     2.6    this.m_name = name;
     2.7 -  this.m_type = fan.sys.Type.find(type);
     2.8 +  this.m_type = (type instanceof fan.sys.Type) ? type : fan.sys.Type.find(type);
     2.9    this.m_hasDefault = hasDefault;
    2.10  }
    2.11  
     3.1 --- a/src/sys/js/fan/Type.js	Wed Feb 15 08:55:43 2012 -0500
     3.2 +++ b/src/sys/js/fan/Type.js	Wed Feb 15 10:38:21 2012 -0500
     3.3 @@ -126,7 +126,7 @@
     3.4  
     3.5  fan.sys.Type.prototype.isGenericParameter = function()
     3.6  {
     3.7 -  return this.pod() == fan.sys.Pod.$sysPod && this.$name().length == 1;
     3.8 +  return this.m_pod.m_name === "sys" && this.m_name.length === 1;
     3.9  }
    3.10  
    3.11  /*
    3.12 @@ -142,7 +142,7 @@
    3.13  }
    3.14  */
    3.15  
    3.16 -fan.sys.Type.isGeneric = function() { return this.isGenericType(); }
    3.17 +fan.sys.Type.prototype.isGeneric = function() { return this.isGenericType(); }
    3.18  
    3.19  /*
    3.20  public Map params()
    3.21 @@ -150,42 +150,43 @@
    3.22    if (noParams == null) noParams = Sys.emptyStrTypeMap;
    3.23    return (Map)noParams;
    3.24  }
    3.25 +*/
    3.26  
    3.27 -public Type parameterize(Map params)
    3.28 +fan.sys.Type.prototype.parameterize = function(params)
    3.29  {
    3.30 -  if (this == Sys.ListType)
    3.31 +  if (this instanceof fan.sys.ListType)
    3.32    {
    3.33 -    Type v = (Type)params.get("V");
    3.34 -    if (v == null) throw ArgErr.make("List.parameterize - V undefined").val;
    3.35 +    var v = params.get("V");
    3.36 +    if (v == null) throw fan.sys.ArgErr.make("List.parameterize - V undefined");
    3.37      return v.toListOf();
    3.38    }
    3.39  
    3.40 -  if (this == Sys.MapType)
    3.41 +  if (this instanceof fan.sys.MapType)
    3.42    {
    3.43 -    Type v = (Type)params.get("V");
    3.44 -    Type k = (Type)params.get("K");
    3.45 -    if (v == null) throw ArgErr.make("Map.parameterize - V undefined").val;
    3.46 -    if (k == null) throw ArgErr.make("Map.parameterize - K undefined").val;
    3.47 -    return new MapType(k, v);
    3.48 +    var v = params.get("V");
    3.49 +    var k = params.get("K");
    3.50 +    if (v == null) throw fan.sys.ArgErr.make("Map.parameterize - V undefined");
    3.51 +    if (k == null) throw fan.sys.ArgErr.make("Map.parameterize - K undefined");
    3.52 +    return new fan.sys.MapType(k, v);
    3.53    }
    3.54  
    3.55 -  if (this == Sys.FuncType)
    3.56 -  {
    3.57 -    Type r = (Type)params.get("R");
    3.58 -    if (r == null) throw ArgErr.make("Map.parameterize - R undefined").val;
    3.59 -    ArrayList p = new ArrayList();
    3.60 -    for (int i='A'; i<='H'; ++i)
    3.61 -    {
    3.62 -      Type x = (Type)params.get(FanStr.ascii[i]);
    3.63 -      if (x == null) break;
    3.64 -      p.add(x);
    3.65 -    }
    3.66 -    return new FuncType((Type[])p.toArray(new Type[p.size()]), r);
    3.67 -  }
    3.68 +  // TODO FIXIT
    3.69 +  // if (this == Sys.FuncType)
    3.70 +  // {
    3.71 +  //   Type r = (Type)params.get("R");
    3.72 +  //   if (r == null) throw ArgErr.make("Map.parameterize - R undefined");
    3.73 +  //   ArrayList p = new ArrayList();
    3.74 +  //   for (int i='A'; i<='H'; ++i)
    3.75 +  //   {
    3.76 +  //     Type x = (Type)params.get(FanStr.ascii[i]);
    3.77 +  //     if (x == null) break;
    3.78 +  //     p.add(x);
    3.79 +  //   }
    3.80 +  //   return new FuncType((Type[])p.toArray(new Type[p.size()]), r);
    3.81 +  // }
    3.82  
    3.83 -  throw UnsupportedErr.make("not generic: " + this).val;
    3.84 +  throw fan.sys.UnsupportedErr.make("not generic: " + this);
    3.85  }
    3.86 -*/
    3.87  
    3.88  fan.sys.Type.prototype.toListOf = function()
    3.89  {
    3.90 @@ -234,31 +235,20 @@
    3.91  
    3.92  fan.sys.Type.prototype.slots = function()
    3.93  {
    3.94 -  // TODO FIXIT: include inheritance; cache
    3.95 -  var acc = [];
    3.96 -  for (var i in this.m_slots)
    3.97 -    acc.push(this.m_slots[i]);
    3.98 -  return fan.sys.List.make(fan.sys.Slot.$type, acc);
    3.99 +  this.doReflect();
   3.100 +  return this.m_slotList;
   3.101  }
   3.102  
   3.103  fan.sys.Type.prototype.methods = function()
   3.104  {
   3.105 -  // TODO FIXIT: include inheritance; cache
   3.106 -  var acc = [];
   3.107 -  for (var i in this.m_slots)
   3.108 -    if (this.m_slots[i] instanceof fan.sys.Method)
   3.109 -      acc.push(this.m_slots[i]);
   3.110 -  return fan.sys.List.make(fan.sys.Method.$type, acc);
   3.111 +  this.doReflect();
   3.112 +  return this.m_methodList;
   3.113  }
   3.114  
   3.115  fan.sys.Type.prototype.fields = function()
   3.116  {
   3.117 -  // TODO FIXIT: include inheritance; cache
   3.118 -  var acc = [];
   3.119 -  for (var i in this.m_slots)
   3.120 -    if (this.m_slots[i] instanceof fan.sys.Field)
   3.121 -      acc.push(this.m_slots[i]);
   3.122 -  return fan.sys.List.make(fan.sys.Field.$type, acc);
   3.123 +  this.doReflect();
   3.124 +  return this.m_fieldList;
   3.125  }
   3.126  
   3.127  fan.sys.Type.prototype.slot = function(name, checked)
   3.128 @@ -453,8 +443,32 @@
   3.129  // Util
   3.130  //////////////////////////////////////////////////////////////////////////
   3.131  
   3.132 +fan.sys.Type.prototype.doReflect = function()
   3.133 +{
   3.134 +  if (this.m_slotList != null) return;
   3.135 +
   3.136 +  var slots = [];
   3.137 +  var fields = [];
   3.138 +  var methods = [];
   3.139 +
   3.140 +  for (var i in this.m_slots)
   3.141 +  {
   3.142 +    slot = this.m_slots[i]
   3.143 +    slots.push(slot);
   3.144 +    if (slot instanceof fan.sys.Field) fields.push(slot);
   3.145 +    else if (slot instanceof fan.sys.Method) methods.push(slot);
   3.146 +  }
   3.147 +
   3.148 +  this.m_slotList = fan.sys.List.make(fan.sys.Slot.$type, slots);
   3.149 +  this.m_fieldList = fan.sys.List.make(fan.sys.Field.$type, fields);
   3.150 +  this.m_methodList = fan.sys.List.make(fan.sys.Method.$type, methods);
   3.151 +}
   3.152 +
   3.153  fan.sys.Type.prototype.$slot = function(name)
   3.154  {
   3.155 +  // reflect
   3.156 +  this.doReflect();
   3.157 +
   3.158    // check self first
   3.159    var slot = this.m_slots[name];
   3.160    if (slot != null) return slot;
   3.161 @@ -575,19 +589,173 @@
   3.162  fan.sys.NullableType.prototype.doc = function() { return this.m_root.doc(); }
   3.163  
   3.164  /*************************************************************************
   3.165 + * GenericType
   3.166 + ************************************************************************/
   3.167 +
   3.168 +fan.sys.GenericType = fan.sys.Obj.$extend(fan.sys.Type)
   3.169 +fan.sys.GenericType.prototype.$ctor = function(v) {}
   3.170 +
   3.171 +fan.sys.GenericType.prototype.doReflect = function()
   3.172 +{
   3.173 +  try
   3.174 +  {
   3.175 +
   3.176 +  if (this.m_slotList != null) return;
   3.177 +
   3.178 +   // ensure master type is reflected
   3.179 +   var master = this.base();
   3.180 +   master.doReflect();
   3.181 +   var masterSlots = master.slots();
   3.182 +
   3.183 +  // allocate slot data structures
   3.184 +  var slots = [];
   3.185 +  var fields = [];
   3.186 +  var methods = [];
   3.187 +
   3.188 +  // parameterize master's slots
   3.189 +  for (var i=0; i<masterSlots.size(); i++)
   3.190 +  {
   3.191 +    var slot = masterSlots.get(i);
   3.192 +    if (slot instanceof fan.sys.Method)
   3.193 +    {
   3.194 +      slot = this.parameterizeMethod(slot);
   3.195 +      methods.push(slot);
   3.196 +    }
   3.197 +    else
   3.198 +    {
   3.199 +      slot = this.parameterizeField(slot);
   3.200 +      fields.push(slot);
   3.201 +    }
   3.202 +    slots.push(slot);
   3.203 +    this.m_slots[slot.m_name] = slot;
   3.204 +  }
   3.205 +
   3.206 +  this.m_slotList = fan.sys.List.make(fan.sys.Slot.$type, slots);
   3.207 +  this.m_fieldList = fan.sys.List.make(fan.sys.Field.$type, fields);
   3.208 +  this.m_methodList = fan.sys.List.make(fan.sys.Method.$type, methods);
   3.209 +
   3.210 +  }
   3.211 +  catch (err)
   3.212 +  {
   3.213 +    println(err);
   3.214 +    println(err.stack);
   3.215 +    println(err.stackTrace);
   3.216 +    println(err.javaException);
   3.217 +  }
   3.218 +}
   3.219 +
   3.220 +fan.sys.GenericType.prototype.parameterizeField = function(f)
   3.221 +{
   3.222 +  // if not generic, short circuit and reuse original
   3.223 +  var t = f.type();
   3.224 +  if (!t.isGenericParameter()) return f;
   3.225 +
   3.226 +  // create new parameterized version
   3.227 +  t = this.parameterizeType(t);
   3.228 +  //var pf = new Field(this, f.name, f.flags, f.facets, f.lineNum, t);
   3.229 +  var pf = new fan.sys.File(this, f.m_name, f.m_flags, f.m_type, f.m_facets);
   3.230 +  //pf.reflect = f.reflect;
   3.231 +  return pf;
   3.232 +}
   3.233 +
   3.234 +fan.sys.GenericType.prototype.parameterizeMethod = function(m)
   3.235 +{
   3.236 +  // if not generic, short circuit and reuse original
   3.237 +  if (!m.isGenericMethod()) return m;
   3.238 +
   3.239 +  // new signature
   3.240 +  var func = m.m_func;
   3.241 +  var ret;
   3.242 +  var params = fan.sys.List.make(fan.sys.Param.$type);
   3.243 +
   3.244 +  // parameterize return type
   3.245 +  if (func.returns().isGenericParameter())
   3.246 +    ret = this.parameterizeType(func.returns());
   3.247 +  else
   3.248 +    ret = func.returns();
   3.249 +
   3.250 +  // narrow params (or just reuse if not parameterized)
   3.251 +  var arity = m.params().size();
   3.252 +  for (var i=0; i<arity; ++i)
   3.253 +  {
   3.254 +    var p = m.params().get(i);
   3.255 +    if (p.m_type.isGenericParameter())
   3.256 +    {
   3.257 +      //params.add(new fan.sys.Param(p.name, parameterize(p.type), p.mask));
   3.258 +      params.add(new fan.sys.Param(p.m_name, this.parameterizeType(p.m_type), p.m_hasDefault));
   3.259 +    }
   3.260 +    else
   3.261 +    {
   3.262 +      params.add(p);
   3.263 +    }
   3.264 +  }
   3.265 +
   3.266 +  //var pm = new Method(this, m.name, m.flags, m.facets, m.lineNum, ret, m.inheritedReturns, params, m);
   3.267 +  var pm = new fan.sys.Method(this, m.m_name, m.m_flags, ret, params, m.m_facets, m)
   3.268 +  //pm.reflect = m.reflect;
   3.269 +  return pm;
   3.270 +}
   3.271 +
   3.272 +fan.sys.GenericType.prototype.parameterizeType = function(t)
   3.273 +{
   3.274 +  var nullable = t.isNullable();
   3.275 +  var nn = t.toNonNullable();
   3.276 +  if (nn instanceof fan.sys.ListType)
   3.277 +    t = this.parameterizeListType(nn);
   3.278 +  //else if (nn instanceof fan.sys.FuncType)
   3.279 +  //  t = parameterizeFuncType((FuncType)nn);
   3.280 +  else
   3.281 +    t = this.doParameterize(nn);
   3.282 +  return nullable ? t.toNullable() : t;
   3.283 +}
   3.284 +
   3.285 +fan.sys.GenericType.prototype.parameterizeListType = function(t)
   3.286 +{
   3.287 +  return this.doParameterize(t.v).toListOf();
   3.288 +}
   3.289 +
   3.290 +// /**
   3.291 +//  * Recursively parameterize the params of a method type.
   3.292 +//  */
   3.293 +// final FuncType parameterizeFuncType(FuncType t)
   3.294 +// {
   3.295 +//   Type[] params = new Type[t.params.length];
   3.296 +//   for (int i=0; i<params.length; ++i)
   3.297 +//   {
   3.298 +//     Type param = t.params[i];
   3.299 +//     if (param.isGenericParameter()) param = doParameterize(param);
   3.300 +//     params[i] = param;
   3.301 +//   }
   3.302 +//
   3.303 +//   Type ret = t.ret;
   3.304 +//   if (ret.isGenericParameter()) ret = doParameterize(ret);
   3.305 +//
   3.306 +//   return new FuncType(params, ret);
   3.307 +// }
   3.308 +
   3.309 +fan.sys.GenericType.prototype.doParameterize = function(t) {}
   3.310 +
   3.311 +/*************************************************************************
   3.312   * ListType
   3.313   ************************************************************************/
   3.314  
   3.315 -fan.sys.ListType = fan.sys.Obj.$extend(fan.sys.Type)
   3.316 +fan.sys.ListType = fan.sys.Obj.$extend(fan.sys.GenericType)
   3.317  fan.sys.ListType.prototype.$ctor = function(v)
   3.318  {
   3.319    this.v = v;
   3.320 +  this.m_qname  = "sys::List";
   3.321 +  this.m_pod    = fan.sys.Pod.find("sys");
   3.322 +  this.m_name   = "List";
   3.323 +  this.m_base   = fan.sys.List.$type;
   3.324    this.m_mixins = fan.sys.Type.$type.emptyList();
   3.325 +  this.m_slots  = {};
   3.326  }
   3.327  
   3.328 -fan.sys.ListType.prototype.base = function() { return fan.sys.List.$type; }
   3.329 -fan.sys.ListType.prototype.signature = function() { return this.v.signature() + '[]'; }
   3.330 -fan.sys.ListType.prototype.$slot = function(name) { return fan.sys.List.$type.$slot(name); }
   3.331 +fan.sys.ListType.prototype.signature = function()
   3.332 +{
   3.333 +  return this.v.signature() + '[]';
   3.334 +}
   3.335 +
   3.336  fan.sys.ListType.prototype.equals = function(that)
   3.337  {
   3.338    if (that instanceof fan.sys.ListType)
   3.339 @@ -636,6 +804,13 @@
   3.340  fan.sys.ListType.prototype.facets = function() { return fan.sys.List.$type.facets(); }
   3.341  fan.sys.ListType.prototype.facet = function(type, checked) { return fan.sys.List.$type.facet(type, checked); }
   3.342  
   3.343 +fan.sys.ListType.prototype.doParameterize = function(t)
   3.344 +{
   3.345 +  if (t == fan.sys.Sys.VType) return this.v;
   3.346 +  if (t == fan.sys.Sys.LType) return this;
   3.347 +  throw new Error(t.toString());
   3.348 +}
   3.349 +
   3.350  /*************************************************************************
   3.351   * MapType
   3.352   ************************************************************************/
   3.353 @@ -646,6 +821,9 @@
   3.354  {
   3.355    this.k = k;
   3.356    this.v = v;
   3.357 +  this.m_qname  = "sys::Map";
   3.358 +  this.m_pod    = fan.sys.Pod.find("sys");
   3.359 +  this.m_name   = "Map";
   3.360    this.m_mixins = fan.sys.Type.$type.emptyList();
   3.361  }
   3.362  
     4.1 --- a/src/sys/js/fanx/TypeParser.js	Wed Feb 15 08:55:43 2012 -0500
     4.2 +++ b/src/sys/js/fanx/TypeParser.js	Wed Feb 15 10:38:21 2012 -0500
     4.3 @@ -45,14 +45,24 @@
     4.4    if (this.cur == '|')
     4.5      type = this.loadFunc();
     4.6  
     4.7 -  // [java] is java FFI
     4.8 -  else if (this.cur == '[' && this.sig.indexOf("[java]") != -1) //sig.regionMatches(pos, "[java]", 0, 6))
     4.9 -    //type = loadFFI();
    4.10 -    throw fan.sys.ArgErr.make("Java types not allowed '" + this.sig + "'");
    4.11 +  // [ is either [ffi]xxx or [K:V] map
    4.12 +  else if (this.cur == '[')
    4.13 +  {
    4.14 +    var ffi = true;
    4.15 +    for (var i=this.pos+1; i<this.len; i++)
    4.16 +    {
    4.17 +      var ch = this.sig.charAt(i);
    4.18 +      if (this.isIdChar(ch)) continue;
    4.19 +      ffi = (ch == ']');
    4.20 +      break;
    4.21 +    }
    4.22  
    4.23 -  // [...] is map
    4.24 -  else if (this.cur == '[')
    4.25 -    type = this.loadMap();
    4.26 +    if (ffi)
    4.27 +      //type = loadFFI();
    4.28 +      throw fan.sys.ArgErr.make("Java types not allowed '" + this.sig + "'");
    4.29 +    else
    4.30 +      type = this.loadMap();
    4.31 +  }
    4.32  
    4.33    // otherwise must be basic[]
    4.34    else
    4.35 @@ -71,6 +81,11 @@
    4.36      this.consume('[');
    4.37      this.consume(']');
    4.38      type = type.toListOf();
    4.39 +    if (this.cur == '?')
    4.40 +    {
    4.41 +      consume('?');
    4.42 +      type = type.toNullable();
    4.43 +    }
    4.44    }
    4.45  
    4.46    // nullable
    4.47 @@ -198,8 +213,7 @@
    4.48      var typeName;
    4.49      try
    4.50      {
    4.51 -      var colon = sig.indexOf(":");
    4.52 -      if (sig.charAt(colon+1) != ':') throw new Exception();
    4.53 +      var colon = sig.indexOf("::");
    4.54        podName  = sig.substring(0, colon);
    4.55        typeName = sig.substring(colon+2);
    4.56        if (podName.length == 0 || typeName.length == 0) throw fan.sys.Err.make("");