Пример #1
0
 public static Stmt ctor(Ctx ctx) {
     Expression<Func<int>> eHashCode = () => hashCode;
     var field = (FieldInfo)((MemberExpression)eHashCode.Body).Member;
     var f = ctx.Module.Import(field);
     var fExpr = new ExprFieldAccess(ctx, null, f).Named("hashCode");
     var stmt = new StmtJsExplicit(ctx, "this.$=++hashCode;", ctx.ThisNamed, fExpr);
     return stmt;
 }
Пример #2
0
 protected virtual ICode VisitJsExplicit(StmtJsExplicit s) {
     this.ThrowOnNoOverride();
     var namedExprs = this.HandleList(s.NamedExprs, x => {
         if (x == null) {
             return null;
         }
         var expr = (Expr)this.Visit(x.Expr);
         return expr == x.Expr ? x : expr.Named(x.Name);
     });
     if (namedExprs != null) {
         return new StmtJsExplicit(s.Ctx, s.JavaScript, namedExprs);
     } else {
         return s;
     }
 }
Пример #3
0
            public Stmt GetImpl(Ctx ctx) {
                var a = ctx.MethodParameter(0).Named("a");
                var b = ctx.MethodParameter(1).Named("b");
                var hi = ctx.Local(ctx.UInt64, "hi");
                var lo = ctx.Local(ctx.UInt64, "lo");
                var limit = ctx.Literal(0x100000000UL, ctx._UInt64, "limit");
                var js = @"
hi = a[0] + b[0];
lo = a[1] + b[1];
if (lo >= limit) {
    lo -= limit;
    hi++;
}
if (hi >= limit) hi -= limit;
return [hi, lo];
";
                var stmt = new StmtJsExplicit(ctx, js, a, b, hi, lo, limit);
                return stmt;
            }
Пример #4
0
            public Stmt GetImpl(Ctx ctx) {
                var a = ctx.MethodParameter(0, "a");
                var b = ctx.MethodParameter(1, "b");
                var hi = ctx.Local(ctx.UInt64, "hi");
                var lo = ctx.Local(ctx.UInt64, "lo");
                var limit = ctx.Literal(0x100000000UL, ctx._UInt64, "limit");
                var js = @"
hi = a[0] - b[0];
lo = a[1] - b[1];
if (lo < 0) {
    lo += limit;
    hi--;
}
if (hi < 0) hi += limit;
return [hi, lo];
";
                var stmt = new StmtJsExplicit(ctx, js, a, b, hi, lo, limit);
                return stmt;
            }
Пример #5
0
        protected virtual ICode VisitJsExplicit(StmtJsExplicit s)
        {
            this.ThrowOnNoOverride();
            var namedExprs = this.HandleList(s.NamedExprs, x => {
                if (x == null)
                {
                    return(null);
                }
                var expr = (Expr)this.Visit(x.Expr);
                return(expr == x.Expr ? x : expr.Named(x.Name));
            });

            if (namedExprs != null)
            {
                return(new StmtJsExplicit(s.Ctx, s.JavaScript, namedExprs));
            }
            else
            {
                return(s);
            }
        }
        public static Stmt Encode(Ctx ctx) {
            var arg = ctx.MethodParameter(0, "arg");
            var id = ctx.Local(ctx.Int32, "id");
            var todo = ctx.Local(ctx.Object, "todo");
            var todoOfs = ctx.Local(ctx.Int32, "todoOfs");
            var enc = ctx.Local(ctx.Object, "enc");
            var obj = ctx.Local(ctx.Object, "obj");
            var json = ctx.Local(ctx.Object, "json");
            var jsonPart = ctx.Local(ctx.Object, "jsonPart");
            var o = ctx.Local(ctx.Object, "o");
            var type = ctx.Local(ctx.Type, "type");
            var oKey = ctx.Local(ctx.String, "oKey");
            var isRoot = ctx.Local(ctx.Boolean, "isRoot");
            var i = ctx.Local(ctx.Int32, "i");
            var isArray = new ExprJsTypeData(ctx, TypeData.IsArray).Named("isArray");
            var jsName = new ExprJsTypeData(ctx, TypeData.JsName).Named("jsName");
            var jsIsDict = new ExprJsTypeData(ctx, TypeData.IsDictionary).Named("jsIsDict");
            var slots = ctx.Local(ctx.Object, "slots");
            var ret = ctx.Local(ctx.Object, "ret");
            // Value-types will be boxed on entry
            var js = @"
try {
id = 0;
if (arg && arg._) {
    arg.$$ = '0';
}
todo = [arg];
todoOfs = 0;
enc = function(o, isRoot) {
    if (o === null || o === undefined) {
        return null;
    }
    var type = o._;
    if (!type) { // Unboxed value-type/primitive
        if (typeof(o) !== 'object' || o instanceof Array) { // primitive number, boolean, string, 64-bit number (array)
            if (typeof(o) === 'number') { // Number
                if (isNaN(o)) {
                    return [0];
                }
                if (o === Number.NEGATIVE_INFINITY) {
                    return [-1];
                }
                if (o === Number.POSITIVE_INFINITY) {
                    return [1];
                }
            }
            return o;
        } else {
            // Non-primitive value-type
            var ret = {};
            for (var oKey in o) {
                ret[oKey] = enc(o[oKey]);
            }
            return ret;
        }
    }
    if (isRoot) {
        // Direct object encoding required
        if (type.jsIsDict) {
            var ret = { '_': type.jsName, 'v': [] };
            var slots = o[type.jsIsDict[0]];
            for (var i=0; i<slots.length; i++) {
                if (slots[i] && slots[i][type.jsIsDict[1]] >= 0) {
                    ret.v.push(enc(slots[i][type.jsIsDict[2]]), enc(slots[i][type.jsIsDict[3]]));
                }
            }
            return ret;
        }
        if (type && type.isArray) {
            var ret = { '_': type.jsName, 'v': [] };
            for (var i=0; i<o.length; i++) {
                ret.v.push(enc(o[i]));
            }
            return ret;
        } else {
            var ret = { '_': type.jsName };
            for (var oKey in o) {
                if (oKey.charAt(0) !== '_' && oKey.charAt(0) !== '$') {
                    ret[oKey] = enc(o[oKey]);
                }
            }
            return ret;
        }
    } else {
        if (!o.$$) {
            o.$$ = (++id).toString();
            todo.push(o);
        }
        return [o.$$];
    }
};
json = [];
while (todo.length > todoOfs) {
    obj = todo[todoOfs++];
    jsonPart = enc(obj, true);
    json.push([(obj && obj.$$) || '0', jsonPart]);
}
for (i = 0; i<todo.length; i++) {
    if (todo[i] && todo[i].$$) {
        delete todo[i].$$;
    }
}
return json;
} catch (eeee) {
console.log('Ex: ' + eeee);
throw eeee;
}
";
            var stmt = new StmtJsExplicit(ctx, js, arg, id, todo, todoOfs, enc, obj, json, jsonPart, o, type, oKey, isRoot, i, isArray, ret, jsName, jsIsDict, slots);
            return stmt;
        }
Пример #7
0
 protected override ICode VisitJsExplicit(StmtJsExplicit s) {
     this.NewLine();
     this.HandleExplicitJs(s.JavaScript, s.NamedExprs);
     return s;
 }
Пример #8
0
            public Stmt GetImpl(Ctx ctx) {
                var a = ctx.MethodParameter(0, "a");
                var b = ctx.MethodParameter(1, "b");
                var aa = ctx.Local(ctx.Int32.MakeArray(), "aa");
                var bb = ctx.Local(ctx.Int32.MakeArray(), "bb");
                var ia = ctx.Local(ctx.Int32, "ia");
                var ib = ctx.Local(ctx.Int32, "ib");
                var mul = ctx.Local(ctx.Int32, "mul");
                var add = ctx.Local(ctx.Int32, "add");
                var mulCarry = ctx.Local(ctx.Int32, "mulCarry");
                var addCarry = ctx.Local(ctx.Int32, "addCarry");
                var rrOfs = ctx.Local(ctx.Int32, "rrOfs");
                var rr = ctx.Local(ctx.Int32.MakeArray(), "rr");
                var mask = ctx.Literal(0xffff, ctx.Int32, "mask");
                var limit = ctx.Literal(0x10000, ctx.Int32, "limit");
                var js = @"
aa = [a[0] >>> 16, a[0] & mask, a[1] >>> 16, a[1] & mask];
bb = [b[0] >>> 16, b[0] & mask, b[1] >>> 16, b[1] & mask];
rr = [0, 0, 0, 0];
for (ib = 3; ib >= 0; ib--) {
    mulCarry = 0;
    addCarry = 0;
    for(ia = 3; ia >= 3 - ib; ia--) {
        rrOfs = ia + ib - 3;
        mul = aa[ia] * bb[ib] + mulCarry;
        mulCarry = mul >>> 16;
        add = rr[rrOfs] + (mul & mask) + addCarry;
        if (add >= limit){
            rr[rrOfs] = add - limit;
            addCarry = 1;
        } else {
            rr[rrOfs] = add;
            addCarry = 0;
        }
    }
}
return [rr[1] + rr[0] * limit, rr[3] + rr[2] * limit];
";
                var stmt = new StmtJsExplicit(ctx, js, a, b, aa, bb, rr, mask, limit, ia, ib, mul, add, mulCarry, addCarry, rrOfs);
                return stmt;
            }
        public static Stmt Decode(Ctx ctx) {
            var arg = ctx.MethodParameter(0, "arg");
            var objs = ctx.Local(ctx.Object, "objs");
            var refs = ctx.Local(ctx.Object, "refs");
            var needDefer = ctx.Local(ctx.Object, "needDefer");
            var defer = ctx.Local(ctx.Object, "defer");
            var dec = ctx.Local(ctx.Object, "dec");
            var type = ctx.Local(ctx.Type, "type");
            var ret = ctx.Local(ctx.Object, "ret");
            var o = ctx.Local(ctx.Object, "o");
            var oArray = ctx.Local(ctx.Object, "oArray");
            var i = ctx.Local(ctx.Int32, "i");
            var isObject = ctx.Local(ctx.Boolean, "isObject");
            var kVal = ctx.Local(ctx.Object, "kVal");
            var vVal = ctx.Local(ctx.Object, "vVal");
            var kRef = ctx.Local(ctx.Boolean, "kRef");
            var vRef = ctx.Local(ctx.Boolean, "vRef");
            var js = @"
objs = {};
refs = [];
needDefer = function(o) {
    return !!(o && o instanceof Array &&  o.length === 1 && typeof(o[0]) === 'string');
};
defer = function(ret, i, o) {
    if (needDefer(o[i])) {
        refs.push(function() {
            ret[i] = objs[o[i][0]];
        });
    } else {
        ret[i] = dec(o[i]);
    }
};
dec = function(o) {
    var ret, i;
    if (o == null) {
        return null;
    }
    var isObject = false;
    if (o._ !== undefined) {
        if (o.__ === 2) { // Dictionary
            ret = $d[o.d][0]({ _: $$[o._] });
            for (var i = 0; i < o.a.length; i++) {
                var kVal = o.a[i];
                var vVal = o.b[i];
                var kRef = needDefer(kVal);
                var vRef = needDefer(vVal);
                (function(kRef, kVal, vRef, vVal, o, ret) {
                    refs.push(function() {
                        if (kRef) kVal = objs[kVal[0]];
                        if (vRef) vVal = objs[vVal[0]];
                        $d[o.d][1](ret, kVal, vVal);
                    });
                })(kRef, kVal, vRef, vVal, o, ret);
            }
        } else if (o['']) { // Array
            var oArray = o[''];
            var ret = new Array(oArray.length);
            ret._ = $$[o._];
            // TODO: Set $
            for (var i = 0; i < oArray.length; i++) {
                defer(ret, i, oArray)
            }
        } else { // Object or boxed struct
            isObject = true;
        }
    } else if (typeof(o) === 'object' && !(o instanceof Array)) { // unboxed value-type
        isObject = true;
    } else if (o instanceof Array && o.length === 1) { // Special Single/Double
        switch (o[0]) {
        case 0: ret = NaN; break;
        case 1: ret = Number.POSITIVE_INFINITY; break;
        case -1: ret = Number.NEGATIVE_INFINITY; break;
        default: throw 'Unrecognised special: ' + o[0];
        }
    } else { // unboxed primitive or null
        ret = o;
    }
    if (isObject) {
        var ret = o._ ? { '_': $$[o._] } : { };
        // TODO: Set $ = hash id
        for (var i in o) {
            if (i !== '_') {
                defer(ret, i, o);
            }
        }
    }
    return ret;
};
for (i = 0; i < arg.length; i++) {
    objs[arg[i][0]] = dec(arg[i][1]);
}
for (i = 0; i < refs.length; i++) {
    refs[i]();
}
ret = objs['0'];
return ret;
";
            var stmt = new StmtJsExplicit(ctx, js, arg, objs, refs, needDefer, defer, dec, type, ret, o, oArray, i, isObject, kVal, vVal, kRef, vRef);
            return stmt;
        }
Пример #10
0
 public static Stmt Equals(Ctx ctx) {
     var other = ctx.MethodParameter(0).Named("other");
     var stmt = new StmtJsExplicit(ctx, "return this===other;", ctx.ThisNamed, other);
     return stmt;
 }
Пример #11
0
 public Stmt GetImpl(Ctx ctx) {
     var a = ctx.MethodParameter(0);
     var b = ctx.MethodParameter(1);
     var divMod = new ExprCall(ctx, (Func<UInt64, UInt64, object>)_Int64UInt64.UInt64DivRem, null, a, b).Named("divMod");
     var js = "return divMod[1];";
     var stmt = new StmtJsExplicit(ctx, js, divMod);
     return stmt;
 }
Пример #12
0
        public static JsResult CreateFrom(IEnumerable <MethodReference> rootMethods, bool verbose = false, bool testing = false)
        {
            var todo = new Queue <MethodReference>();

            foreach (var method in rootMethods)
            {
                todo.Enqueue(method);
            }
            Action <MethodReference> addTodo = m => {
                if (m.ContainsGenericParameters())
                {
                    throw new Exception("Cannot add todo method with generic parameters");
                }
                todo.Enqueue(m);
            };
            // Each method, with the count of how often it is referenced.
            var methodsSeen = new Dictionary <MethodReference, int>(rootMethods.ToDictionary(x => x, x => 1), TypeExtensions.MethodRefEqComparerInstance);
            // Each type, with the count of how often it is referenced directly (newobj only at the moment).
            var typesSeen = new Dictionary <TypeReference, int>(TypeExtensions.TypeRefEqComparerInstance);
            // ASTs of all methods
            var methodAsts = new Dictionary <MethodReference, ICode>(TypeExtensions.MethodRefEqComparerInstance);
            // Each field, with the count of how often it is referenced.
            var fieldAccesses = new Dictionary <FieldReference, int>(TypeExtensions.FieldReqEqComparerInstance);
            // Each type records which virtual methods have their NewSlot definitions
            var virtualCalls = new Dictionary <TypeReference, HashSet <MethodReference> >(TypeExtensions.TypeRefEqComparerInstance);
            // Each basemost virtual method records the least-derived type actually called
            // This allows only virtual methods in more-derived types to be transcoded
            var virtualCallExactMethods = new Dictionary <MethodReference, IEnumerable <TypeReference> >(TypeExtensions.MethodRefEqComparerInstance);
            // Each interface type records which interface methods are called
            var interfaceCalls = new Dictionary <TypeReference, HashSet <MethodReference> >(TypeExtensions.TypeRefEqComparerInstance);
            // All instance constructors must be updated after all methods have been processed, to initialise all referenced
            // instance fields in the type, and to sort out 'return' statements.
            // This has to be done later, so the list of referenced fields in complete
            var instanceConstructors = new List <Ctx>();

            while (todo.Any())
            {
                // TODO: parallelise
                var mRef = todo.Dequeue();
                var mDef = mRef.Resolve();
                var tRef = mRef.DeclaringType;
                var tDef = tRef.Resolve();
                var ctx  = new Ctx(tRef, mRef);
                var ast  = (ICode)JsResolver.ResolveMethod(ctx);
                if (ast == null)
                {
                    var transcodeCtx = JsResolver.TranslateCtx(ctx) ?? ctx;
                    if (transcodeCtx.MRef.ContainsGenericParameters())
                    {
                        throw new InvalidOperationException("Method/type must not have generic parameters");
                    }
                    if (transcodeCtx.MDef.IsAbstract)
                    {
                        throw new InvalidOperationException("Cannot transcode an abstract method");
                    }
                    if (transcodeCtx.MDef.IsInternalCall)
                    {
                        throw new InvalidOperationException("Cannot transcode an internal call");
                    }
                    if (transcodeCtx.MDef.IsExternal())
                    {
                        throw new InvalidOperationException("Cannot transcode an external method");
                    }
                    if (!transcodeCtx.MDef.HasBody)
                    {
                        throw new InvalidOperationException("Cannot transcode method without body");
                    }

                    if (!typesSeen.ContainsKey(tRef))
                    {
                        typesSeen.Add(tRef, 0);
                    }
                    ast = Transcoder.ToAst(transcodeCtx, verbose);
                }

                for (int i = 0; ; i++)
                {
                    var astOrg = ast;
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsRewriteSealedVCalls.V(s), (Stmt)ast, "VisitorJsRewriteSealedVCalls", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveAll.V(s), (Stmt)ast, "VisitorJsResolveAll", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveConv.V(s), (Stmt)ast, "VisitorJsResolveConv", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveSpecialTypes.V(s), (Stmt)ast, "VisitorJsResolveSpecialTypes", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveDelegates.V(s), (Stmt)ast, "VisitorJsResolveDelegates", verbose);
                    // 64bit must be after everything else
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolve64Bit.V(s), (Stmt)ast, "VisitorJsResolve64Bit", verbose);
                    if (ast == astOrg)
                    {
                        break;
                    }
                    if (i > 10)
                    {
                        // After 10 iterations even the most complex method should be sorted out
                        throw new InvalidOperationException("Error: Stuck in loop trying to resolve AST");
                    }
                }
                ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveValueTypes.V(s), (Stmt)ast, "VisitorJsResolveValueTypes", verbose);
                ast = Transcoder.DoStep(s => (Stmt)VisitorIfSimplification.V(s), (Stmt)ast, "VisitorIfSimplification", verbose);
                ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveByRefParameters.V(s), (Stmt)ast, "VisitorJsResolveByRefParameters", verbose);

                if (mDef.IsVirtual && mRef.DeclaringType.IsValueType)
                {
                    // 'this' may be boxed or unboxed. Must be unboxed if boxed
                    // This is required because in real .NET the boxed and unboxed versions are both directly
                    // available at the this reference; this is not the case in the JS emulation of boxing
                    var unbox = new StmtJsExplicit(ctx, "if (this._) this = this.v;", ctx.ThisNamed);
                    ast = new StmtBlock(ctx, unbox, (Stmt)ast);
                    Transcoder.Print((Stmt)ast, "Unbox-this", verbose);
                }

                if (mDef.IsConstructor && !mDef.IsStatic)
                {
                    // Instance constructor; add instance field initialisation and final return of 'this' later
                    instanceConstructors.Add(ctx);
                }

                var cctors = VisitorFindStaticConstructors.V(ast)
                             .Where(x => !TypeExtensions.MethodRefEqComparerInstance.Equals(x, mRef))
                             .Distinct(TypeExtensions.MethodRefEqComparerInstance)
                             .ToArray();
                if (cctors.Any())
                {
                    // All methods that access static fields or methods must call the static constructor at the very
                    // start of the method. Except the static construtor itself, which must not recurse into itself.
                    var cctorCalls = cctors
                                     .Select(x => new StmtWrapExpr(ctx, new ExprCall(ctx, x, null, Enumerable.Empty <Expr>(), false))).ToArray();
                    ast = new StmtBlock(ctx, cctorCalls.Concat((Stmt)ast));
                    Transcoder.Print((Stmt)ast, "Call-cctors", verbose);
                }

                if (mDef.IsConstructor && mDef.IsStatic)
                {
                    // At the beginning of the static constructor, it rewrites itself as an empty function, so it is only called once.
                    var rewrite = new StmtAssignment(ctx, new ExprJsVarMethodReference(ctx, mRef), new ExprJsEmptyFunction(ctx));
                    ast = new StmtBlock(ctx, rewrite, (Stmt)ast);
                    Transcoder.Print((Stmt)ast, "cctor-once-only", verbose);
                }

                methodAsts.Add(mRef, ast);

                var fieldRefs = VisitorFindFieldAccesses.V(ast);
                foreach (var fieldRef in fieldRefs)
                {
                    fieldAccesses[fieldRef] = fieldAccesses.ValueOrDefault(fieldRef) + 1;
                }
                var arrayRefs = VisitorFindNewArrays.V(ast);
                foreach (var arrayRef in arrayRefs)
                {
                    typesSeen[arrayRef] = typesSeen.ValueOrDefault(arrayRef) + 1;
                }
                var types = VisitorFindRequiredTypes.V(ast);
                foreach (var type in types)
                {
                    typesSeen[type] = typesSeen.ValueOrDefault(type) + 1;
                }
                if (mDef.GetCustomAttribute <JsReturnTypeDeepUseAttribute>(true) != null)
                {
                    var retType  = mDef.ReturnType.FullResolve(ctx);
                    var retTypes = retType.EnumThisAllContainedTypes().ToArray();
                    foreach (var type in retTypes)
                    {
                        typesSeen[type] = typesSeen.ValueOrDefault(type) + 1;
                        if (type.IsGenericInstance && type.Resolve().FullName == "System.Collections.Generic.Dictionary`2")
                        {
                            // HACK - ensure no-arg ctor is present. JSON needs it
                            var ctor = type.EnumResolvedMethods().First(x => x.Name == ".ctor" && !x.HasParameters);
                            if (!methodsSeen.ContainsKey(ctor))
                            {
                                methodsSeen.Add(ctor, 1);
                                addTodo(ctor);
                            }
                            // HACK - ensure Add(key, value) method present. JSON need sit
                            var mAdd = type.EnumResolvedMethods().First(x => x.Name == "Add");
                            if (!methodsSeen.ContainsKey(mAdd))
                            {
                                methodsSeen.Add(mAdd, 1);
                                addTodo(mAdd);
                            }
                        }
                    }
                }

                var calledMethods = new List <ICall>();
                var calls         = VisitorFindCalls.V(ast);
                foreach (var call in calls.Where(x => x.ExprType == Expr.NodeType.NewObj || x.IsVirtualCall))
                {
                    // Add reference to each type constructed (direct access to type variable)
                    typesSeen[call.Type] = typesSeen.ValueOrDefault(call.Type) + 1;
                }
                foreach (var call in calls)
                {
                    if (call.CallMethod.DeclaringType.Resolve().IsInterface)
                    {
                        var methodSet = interfaceCalls.ValueOrDefault(call.CallMethod.DeclaringType, () => new HashSet <MethodReference>(TypeExtensions.MethodRefEqComparerInstance), true);
                        methodSet.Add(call.CallMethod);
                        // Methods that require transcoding are added to 'todo' later
                        continue;
                    }
                    if (call.IsVirtualCall)
                    {
                        var mBasemost = call.CallMethod.GetBasemostMethod(null);
                        var methodSet = virtualCalls.ValueOrDefault(mBasemost.DeclaringType, () => new HashSet <MethodReference>(TypeExtensions.MethodRefEqComparerInstance), true);
                        methodSet.Add(mBasemost);
                        var objType = call.Obj.Type;
                        var already = virtualCallExactMethods.ValueOrDefault(mBasemost).EmptyIfNull();
                        if (!already.Any(x => x.IsBaseOfOrEqual(objType)))
                        {
                            virtualCallExactMethods[mBasemost] = already.Concat(objType).ToArray();
                        }
                        // Methods that require transcoding are added to 'todo' later
                        continue;
                    }
                    calledMethods.Add(call);
                }
                foreach (var call in calledMethods)
                {
                    if (methodsSeen.ContainsKey(call.CallMethod))
                    {
                        methodsSeen[call.CallMethod]++;
                    }
                    else
                    {
                        methodsSeen.Add(call.CallMethod, 1);
                        //todo.Enqueue(call.CallMethod);
                        addTodo(call.CallMethod);
                    }
                }

                if (!todo.Any())
                {
                    // Add System.RuntimeType if any types have been seen
                    if (typesSeen.Any(x => x.Value > 0) && !typesSeen.Any(x => x.Key.FullName == "System.RuntimeType"))
                    {
                        var runtimeType = ctx.Module.Import(Type.GetType("System.RuntimeType"));
                        typesSeen.Add(runtimeType, 1);
                    }
                    // Scan all virtual calls and add any required methods
                    // Need care to handle virtual methods with generic arguments
                    var virtualRoots   = new HashSet <MethodReference>(virtualCalls.SelectMany(x => x.Value), TypeExtensions.MethodRefEqComparerInstance);
                    var requireMethods =
                        from type in typesSeen.Keys
                        //let typeReverseMapped = JsResolver.ReverseTypeMap(type)
                        let typeAndBases = type.EnumThisAllBaseTypes().ToArray()
                                           let mVRoots = typeAndBases.SelectMany(x => virtualCalls.ValueOrDefault(x).EmptyIfNull()).ToArray()
                                                         let methods = type.EnumResolvedMethods(mVRoots).ToArray()
                                                                       from method in methods                  //.Select(x => JsResolver.ResolveMethod(x))
                                                                       let methodDef = method.Resolve()
                                                                                       where methodDef != null // HACK?
                                                                                       where !methodDef.IsStatic && methodDef.IsVirtual && !methodDef.IsAbstract
                                                                                       where !methodsSeen.ContainsKey(method)
                                                                                       let mBasemost = method.GetBasemostMethod(method)
                                                                                                       where virtualCallExactMethods.ValueOrDefault(mBasemost).EmptyIfNull().Any(x => {
                        //return x.IsBaseOfOrEqual(typeReverseMapped) || typeReverseMapped.IsBaseOfOrEqual(x);
                        return(x.IsBaseOfOrEqual(type) || type.IsBaseOfOrEqual(x));
                    })
                                                                                                       where virtualRoots.Contains(mBasemost)
                                                                                                       select method;
                    var requireMethodsArray = requireMethods.Distinct(TypeExtensions.MethodRefEqComparerInstance).ToArray();
                    foreach (var method in requireMethodsArray)
                    {
                        methodsSeen.Add(method, 1); // TODO: How to properly handle count?
                        //todo.Enqueue(method);
                        addTodo(method);
                    }
                    // Scan all interface calls and add any required methods
                    var iFaceMethods =
                        from type in typesSeen.Keys
                        from iFace in interfaceCalls
                        let iFaceType = iFace.Key
                                        let typeAndBases = type.EnumThisAllBaseTypes().ToArray()
                                                           where typeAndBases.Any(x => x.DoesImplement(iFaceType))
                                                           let methods = typeAndBases.SelectMany(x => x.EnumResolvedMethods(iFace.Value)).ToArray()
                                                                         from method in methods//.Select(x => JsResolver.ResolveMethod(x))
                                                                         where !methodsSeen.ContainsKey(method)
                                                                         let methodDef = method.Resolve()
                                                                                         where methodDef != null // HACK?
                                                                                         where !methodDef.IsStatic && methodDef.IsVirtual && !methodDef.IsAbstract
                                                                                         from iFaceMethod in iFace.Value
                                                                                         where method.IsImplementationOf(iFaceMethod)
                                                                                         select method;
                    var iFaceMethodsArray = iFaceMethods.Distinct(TypeExtensions.MethodRefEqComparerInstance).ToArray();
                    foreach (var method in iFaceMethodsArray)
                    {
                        methodsSeen.Add(method, 1);
                        addTodo(method);
                        //todo.Enqueue(method);
                    }
                }
            }

            // Add type count to System.RuntimeType
            var runtimeTypeInc = typesSeen.Keys.FirstOrDefault(x => x.FullName == "System.RuntimeType");

            if (runtimeTypeInc != null)
            {
                typesSeen[runtimeTypeInc] += 2;
            }

            var instanceFieldsByType = fieldAccesses
                                       .Where(x => !x.Key.Resolve().IsStatic)
                                       .ToLookup(x => x.Key.DeclaringType.FullResolve(x.Key), TypeExtensions.TypeRefEqComparerInstance);

            // Update all instance constructors to initialise instance fields, add final 'return' statement,
            // and update any early returns to return 'this'
            foreach (var ctx in instanceConstructors)
            {
                var fields    = instanceFieldsByType[ctx.TRef].Select(x => x.Key);
                var initStmts = fields.Select(x => {
                    var f      = x.FullResolve(ctx.TRef, ctx.MRef);
                    var assign = new StmtAssignment(ctx,
                                                    new ExprFieldAccess(ctx, ctx.This, f),
                                                    new ExprDefaultValue(ctx, f.FieldType));
                    return(assign);
                }).ToArray();
                var returnStmt = new StmtReturn(ctx, ctx.This);
                var ast        = methodAsts[ctx.MRef];
                ast = new StmtBlock(ctx, initStmts.Concat((Stmt)ast).Concat(returnStmt));
                ast = VisitorJsReturnThis.V(ast, ctx.This);
                methodAsts[ctx.MRef] = ast;
            }

            // Locally name all instance fields; base type names must not be re-used in derived types
            var instanceFieldsIndexed = new Dictionary <int, Tuple <IEnumerable <FieldReference>, int> >(); // <index, Tuple<all fields, total use count>>

            instanceFieldsByType.TypeTreeTraverse(x => x.Key, (fields, idx) => {
                var ordered = fields.OrderByDescending(x => x.Value).ToArray(); // Order by usage count, highest first
                foreach (var field in ordered)
                {
                    var idxInfo                = instanceFieldsIndexed.ValueOrDefault(idx, () => Tuple.Create(Enumerable.Empty <FieldReference>(), 0));
                    var newIdxInfo             = Tuple.Create((IEnumerable <FieldReference>)idxInfo.Item1.Concat(field.Key).ToArray(), idxInfo.Item2 + field.Value);
                    instanceFieldsIndexed[idx] = newIdxInfo;
                    idx++;
                }
                return(idx);
            }, 0);
            var orderedInstanceFields = instanceFieldsIndexed.OrderByDescending(x => x.Value.Item2).ToArray();
            var instanceFieldNameGen  = new NameGenerator();
            var instanceFieldNames    = orderedInstanceFields
                                        .Select(x => new { name = instanceFieldNameGen.GetNewName(), fields = x.Value.Item1 })
                                        .SelectMany(x => x.fields.Select(y => new { f = y, name = x.name }))
                                        .ToArray();
            // Prepare list of static fields for global naming
            var staticFields = fieldAccesses.Where(x => x.Key.Resolve().IsStatic).ToArray();

            // Prepare local variables for global naming.
            // All locals in all methods are sorted by usage count, then all methods usage counts are combined
            var clusters      = methodAsts.Values.SelectMany(x => VisitorJsPhiClusters.V(x).Select(y => new ExprVarCluster(y))).ToArray();
            var varToCluster  = clusters.SelectMany(x => x.Vars.Select(y => new { cluster = x, var = y })).ToDictionary(x => x.var, x => x.cluster);
            var varsWithCount = methodAsts.Values.Select(x => {
                var methodVars = VisitorFindVars.V(x);
                // Parameters need one extra count, as they appear in the method declaration
                methodVars = methodVars.Concat(methodVars.Where(y => y.ExprType == Expr.NodeType.VarParameter).Distinct());
                var ret    = methodVars.Select(y => varToCluster.ValueOrDefault(y) ?? y)
                             .GroupBy(y => y)
                             .Select(y => new { var = y.Key, count = y.Count() })
                             .OrderByDescending(y => y.count)
                             .ToArray();
                return(ret);
            }).ToArray();
            var localVarCounts = new Dictionary <int, int>();

            foreach (var x in varsWithCount)
            {
                for (int i = 0; i < x.Length; i++)
                {
                    localVarCounts[i] = localVarCounts.ValueOrDefault(i) + x[i].count;
                }
            }

            // Globally name all items that require names
            var needNaming =
                localVarCounts.Select(x => new { item = (object)x.Key, count = x.Value })
                .Concat(methodsSeen.Select(x => new { item = (object)x.Key, count = x.Value }))
                .Concat(staticFields.Select(x => new { item = (object)x.Key, count = x.Value }))
                .Concat(typesSeen.Select(x => new { item = (object)x.Key, count = x.Value }))
                .Where(x => x.count > 0)
                .OrderByDescending(x => x.count)
                .ToArray();
            var nameGen     = new NameGenerator();
            var globalNames = needNaming.ToDictionary(x => x.item, x => nameGen.GetNewName());

            // Create map of all local variables to their names
            var localVarNames = varsWithCount.Select(x => x.Select((y, i) => new { y.var, name = globalNames[i] }))
                                .SelectMany(x => x)
                                .SelectMany(x => {
                var varCluster = x.var as ExprVarCluster;
                if (varCluster != null)
                {
                    return(varCluster.Vars.Select(y => new { var = y, name = x.name }));
                }
                else
                {
                    return(new[] { x });
                }
            })
                                .ToDictionary(x => x.var, x => x.name);

            // Create map of all method names
            var methodNames = methodsSeen.Keys.ToDictionary(x => x, x => globalNames[x], TypeExtensions.MethodRefEqComparerInstance);

            // Create list of all static field names
            var staticFieldNames = staticFields.Select(x => new { f = x.Key, name = globalNames[x.Key] });

            // Create map of all fields
            if (testing)
            {
                instanceFieldNames = instanceFieldNames.Select(x => {
                    switch (x.f.FullName)
                    {
                    case "System.String System.Exception::_message":
                        return(new { x.f, name = "$$_message" });
                    }
                    return(x);
                }).ToArray();
            }
            var fieldNames = instanceFieldNames.Concat(staticFieldNames).ToDictionary(x => x.f, x => x.name, TypeExtensions.FieldReqEqComparerInstance);
            // Create map of type names
            var typeNames = typesSeen
                            .Where(x => x.Value > 0)
                            .ToDictionary(x => x.Key, x => globalNames[x.Key], TypeExtensions.TypeRefEqComparerInstance);

            // Create virtual call tables
            var virtualCallIndices = new Dictionary <MethodReference, int>(TypeExtensions.MethodRefEqComparerInstance);
            var allVirtualMethods  = new Dictionary <TypeReference, HashSet <MethodReference> >(TypeExtensions.TypeRefEqComparerInstance);

            typesSeen.Select(x => x.Key).TypeTreeTraverse(x => x, (type, vCalls) => {
                var mNewSlots = virtualCalls.ValueOrDefault(type).EmptyIfNull().ToArray();
                int idx       = vCalls.Length;
                foreach (var mNewSlot in mNewSlots)
                {
                    virtualCallIndices[mNewSlot] = idx++;
                }
                var vCallsWithThisType = vCalls.Concat(mNewSlots).ToArray();
                if (vCallsWithThisType.Length > 0)
                {
                    var typesAndBases = type.EnumThisAllBaseTypes().ToArray();
                    var mVRoots       = typesAndBases.SelectMany(x => virtualCalls.ValueOrDefault(x).EmptyIfNull()).ToArray();
                    var ms            = type.EnumResolvedMethods(mVRoots).ToArray();
                    for (int i = 0; i < vCalls.Length; i++)
                    {
                        var mVCall = vCallsWithThisType[i];
                        foreach (var m in ms)
                        {
                            if (m.MatchMethodOnly(mVCall))
                            {
                                vCallsWithThisType[i] = m;
                            }
                        }
                    }
                    var typeVMethods = new HashSet <MethodReference>(vCallsWithThisType, TypeExtensions.MethodRefEqComparerInstance);
                    allVirtualMethods.Add(type, typeVMethods);
                }
                return(vCallsWithThisType);
            }, new MethodReference[0]);

            var typeData = Enum.GetValues(typeof(TypeData)).Cast <TypeData>().ToArray();

            // Name all items that are within the type information
            var needTypeInformationNaming =
                interfaceCalls.Select(x => new { item = (object)x.Key, count = 1 })
                .Concat(typeData.Select(x => new { item = (object)x, count = 1 }))
                .OrderByDescending(x => x.count)
                .ToArray();
            var typeInformationNameGen = new NameGenerator();
            var typeInformationNames   = needTypeInformationNaming.ToDictionary(x => x.item, x => typeInformationNameGen.GetNewName());

            if (testing)
            {
                typeInformationNames[TypeData.Name]      = "$$TypeName";
                typeInformationNames[TypeData.Namespace] = "$$TypeNamespace";
            }

            // Create map of interfaces to their names
            var interfaceNames       = interfaceCalls.Keys.ToDictionary(x => x, x => typeInformationNames[x], TypeExtensions.TypeRefEqComparerInstance);
            var interfaceCallIndices = interfaceCalls.SelectMany(x => x.Value.Select((m, i) => new { m, i })).ToDictionary(x => x.m, x => x.i, TypeExtensions.MethodRefEqComparerInstance);

            // Create map of type data constants
            var typeDataNames = typeData.ToDictionary(x => x, x => typeInformationNames[x]);

            var resolver = new JsMethod.Resolver {
                LocalVarNames        = localVarNames,
                MethodNames          = methodNames,
                FieldNames           = fieldNames,
                TypeNames            = typeNames,
                VirtualCallIndices   = virtualCallIndices,
                InterfaceCallIndices = interfaceCallIndices,
                InterfaceNames       = interfaceNames,
                TypeDataNames        = typeDataNames,
            };

            var js = new StringBuilder();

            js.Append("(function(){");
            int    jsIndent  = 1;
            Action jsNewLine = () => {
                js.AppendLine();
                js.Append(' ', jsIndent * JsMethod.tabSize);
            };

            jsNewLine();
            js.Append("\"use strict\";");
            jsNewLine();
            // Construct methods
            foreach (var methodInfo in methodAsts)
            {
                var mRef     = methodInfo.Key;
                var ast      = methodInfo.Value;
                var mJs      = JsMethod.Create(mRef, resolver, ast);
                var mJsLines = mJs.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
                foreach (var line in mJsLines)
                {
                    jsNewLine();
                    js.Append(line);
                }
            }

            // Construct static fields
            foreach (var field in staticFields.Select(x => x.Key))
            {
                jsNewLine();
                js.AppendFormat("// {0}", field.FullName);
                jsNewLine();
                if (field.Name == "Empty" && field.DeclaringType.FullName == "System.String")
                {
                    // Special case, as string does not have a static constructor to set String.Empty
                    js.AppendFormat("var {0} = \"\";", fieldNames[field]);
                }
                else
                {
                    js.AppendFormat("var {0} = {1};", fieldNames[field], DefaultValuer.Get(field.FieldType, fieldNames));
                }
            }

            // Construct type data
            var typesSeenOrdered = typesSeen
                                   .Where(x => x.Value > 0)
                                   .Select(x => x.Key)
                                   .OrderByReferencedFirst(x => x)
                                   .ToArray();
            var domTypes = new Dictionary <string, TypeReference>();

            foreach (var type in typesSeenOrdered)
            {
                var unmappedType = type;
                var tDef         = unmappedType.Resolve();
                // Check for DOM types
                var jsClassAttr = tDef.GetCustomAttribute <JsClassAttribute>();
                if (jsClassAttr != null)
                {
                    if (jsClassAttr.ConstructorArguments.Count == 1)
                    {
                        // Non-abstract types only
                        var tagOrConstructorName = (string)jsClassAttr.ConstructorArguments[0].Value;
                        domTypes.Add(tagOrConstructorName, unmappedType);
                    }
                }
                // Type JS
                jsNewLine();
                js.AppendFormat("// {0}", unmappedType.FullName);
                jsNewLine();
                js.AppendFormat("var {0}={{", typeNames[type]);
                // Type information
                js.AppendFormat("{0}:\"{1}\"", typeDataNames[TypeData.JsName], typeNames[type]);
                js.AppendFormat(", {0}:\"{1}\"", typeDataNames[TypeData.Name], unmappedType.Name());
                js.AppendFormat(", {0}:\"{1}\"", typeDataNames[TypeData.Namespace], unmappedType.Namespace);
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsValueType], unmappedType.IsValueType ? "true" : "false");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsPrimitive], unmappedType.IsPrimitive ? "true" : "false");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsArray], unmappedType.IsArray ? "true" : "false");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.ElementType], unmappedType.IsArray ? typeNames.ValueOrDefault(((ArrayType)unmappedType).ElementType, "null") : "null");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsInterface], tDef.IsInterface ? "true" : "false");
                var assignableTo = typesSeenOrdered.Where(x => unmappedType.IsAssignableTo(x)).Where(x => !x.IsSame(unmappedType)).ToArray();
                js.AppendFormat(", {0}:[{1}]", typeDataNames[TypeData.AssignableTo], string.Join(", ", assignableTo.Select(x => typeNames[x])));
                if (tDef.FullName == "System.Collections.Generic.Dictionary`2")
                {
                    var typeGen        = (GenericInstanceType)type;
                    var dict           = tDef.Module.Import(typeof(DotNetWebToolkit.Cil2Js.JsResolvers.Classes._Dictionary <,>));
                    var dictGen        = dict.MakeGeneric(typeGen.GenericArguments[0], typeGen.GenericArguments[1]);
                    var jsSlotsName    = fieldNames[dictGen.EnumResolvedFields().First(x => x.Name == "slots")];
                    var slot           = dictGen.Resolve().NestedTypes.First(x => x.Name == "Slot");
                    var slotGen        = slot.MakeGeneric(typeGen.GenericArguments[0], typeGen.GenericArguments[1]);
                    var jsHashCodeName = fieldNames[slotGen.EnumResolvedFields().First(x => x.Name == "hashCode")];
                    var jskeyName      = fieldNames[slotGen.EnumResolvedFields().First(x => x.Name == "key")];
                    var jsValueName    = fieldNames[slotGen.EnumResolvedFields().First(x => x.Name == "value")];
                    js.AppendFormat(", {0}:['{1}','{2}','{3}','{4}']", typeDataNames[TypeData.IsDictionary], jsSlotsName, jsHashCodeName, jskeyName, jsValueName);
                }
                if (!tDef.IsInterface)
                {
                    if (!tDef.IsAbstract)
                    {
                        // Virtual method table, only needed on concrete types
                        var typeAndBases = type.EnumThisAllBaseTypes().ToArray();
                        var methods      = allVirtualMethods.ValueOrDefault(type);
                        if (methods != null)
                        {
                            var idxs = methods
                                       .Select(x => {
                                var xBasemost = x.GetBasemostMethod(x);
                                return(new { m = x, idx = virtualCallIndices[xBasemost] });
                            })
                                       .OrderBy(x => x.idx)
                                       .ToArray();
                            var s = string.Join(", ", idxs.Select(x => methodNames.ValueOrDefault(x.m, "null")));
                            js.AppendFormat(", {0}:[{1}]", typeDataNames[TypeData.VTable], s);
                        }
                        // Interface tables, only needed on concrete types
                        var implementedIFaces = interfaceCalls.Where(x => typeAndBases.Any(y => y.DoesImplement(x.Key))).ToArray();
                        foreach (var iFace in implementedIFaces)
                        {
                            js.Append(", ");
                            var iFaceName = interfaceNames[iFace.Key];
                            js.AppendFormat("{0}:[", iFaceName);
                            var qInterfaceTableNames =
                                from iMethod in iFace.Value
                                let tMethod = typeAndBases.SelectMany(x => x.EnumResolvedMethods(iMethod)).First(x => x.IsImplementationOf(iMethod))
                                              //let tM2 = JsResolver.ResolveMethod(tMethod)
                                              let idx = interfaceCallIndices[iMethod]
                                                        orderby idx
                                                        let methodName = methodNames[tMethod]
                                                                         select methodName;
                            var interfaceTableNames = qInterfaceTableNames.ToArray();
                            js.Append(string.Join(", ", interfaceTableNames));
                            js.Append("]");
                        }
                    }
                }
                if (tDef.IsEnum && tDef.GetCustomAttribute <JsStringEnumAttribute>() != null)
                {
                    // JS string/enum map
                    var values = tDef.Fields.Where(x => x.IsLiteral).Select(x => {
                        return(string.Format("{0}:\"{1}\",\"{1}\":{0}", x.Constant, JsResolver.JsName(x)));
                    }).ToArray();
                    js.AppendFormat(", {0}:{{{1}}}", typeDataNames[TypeData.EnumStringMap], string.Join(", ", values));
                }
                // end
                js.Append("};");
            }
            // Add type of each type, if System.RuntimeType has been seen
            var typeRuntimeType = typesSeen.Keys.FirstOrDefault(x => x.FullName == "System.RuntimeType");

            if (typeRuntimeType != null)
            {
                jsNewLine();
                foreach (var type in typesSeenOrdered)
                {
                    js.Append(typeNames[type]);
                    js.Append("._ = ");
                }
                js.Append(typeNames[typeRuntimeType]);
                js.Append(";");
            }
            // Add comments descibing each interface
            jsNewLine();
            js.Append("// Interface name map");
            jsNewLine();
            js.AppendFormat("// {0} = VTable", typeDataNames[TypeData.VTable]);
            foreach (var iFace in interfaceNames)
            {
                jsNewLine();
                js.AppendFormat("// {0} = {1}", iFace.Value, iFace.Key.FullName);
            }
            // Add map of DOM types
            if (domTypes.Any())
            {
                jsNewLine();
                js.Append("// DOM type mapping");
                jsNewLine();
                // TODO: Auto-name this
                js.Append("var __ = {");
                jsIndent++;
                foreach (var domType in domTypes)
                {
                    jsNewLine();
                    js.AppendFormat("'{0}': {1},", domType.Key, typeNames[domType.Value]);
                }
                js.Length--;
                jsIndent--;
                jsNewLine();
                js.Append("};");
            }
            if (typesSeenOrdered.Any())
            {
                jsNewLine();
                js.Append("// json type mapping");
                jsNewLine();
                // TODO: Auto-name this
                js.Append("var $$ = {");
                foreach (var type in typesSeenOrdered)
                {
                    js.AppendFormat("'{0}':{0},", typeNames[type]);
                }
                js.Length--;
                js.Append("};");
                var typesDicts = typesSeenOrdered
                                 .Where(x => x.IsGenericInstance && x.Resolve().FullName == "System.Collections.Generic.Dictionary`2")
                                 .ToArray();
                if (typesDicts.Any())
                {
                    jsNewLine();
                    js.Append("// json dictionary info");
                    jsNewLine();
                    // TODO: Auto-name or get rid of this
                    js.Append("var $d = {");
                    var any = false;
                    foreach (var type in typesDicts)
                    {
                        var typeName = typeNames[type];
                        var ctor     = type.EnumResolvedMethods().First(x => x.Name == ".ctor" && !x.HasParameters);
                        var mAdd     = type.EnumResolvedMethods().First(x => x.Name == "Add");
                        // If dict not involved in JSON, these methods may not be present
                        if (methodNames.ContainsKey(ctor) && methodNames.ContainsKey(mAdd))
                        {
                            var ctorName = methodNames[ctor];
                            var mAddName = methodNames[mAdd];
                            js.AppendFormat("'{0}':[{1},{2}],", typeName, ctorName, mAddName);
                            any = true;
                        }
                    }
                    if (any)
                    {
                        js.Length--;
                    }
                    js.Append("};");
                }
            }

            jsNewLine();
            jsNewLine();
            js.Append("// Exports");
            if (!testing)
            {
                var rootMethodsByType           = rootMethods.ToLookup(x => x.DeclaringType, TypeExtensions.TypeRefEqComparerInstance);
                Action <NamespaceTree> treeToJs = null;
                treeToJs = tree => {
                    js.Append("{");
                    jsIndent++;
                    foreach (var subNs in tree.Namespaces)
                    {
                        jsNewLine();
                        js.AppendFormat("'{0}': ", subNs.NamespacePart);
                        treeToJs(subNs);
                    }
                    if (tree.Types.Any())
                    {
                        foreach (var type in tree.Types)
                        {
                            jsNewLine();
                            js.AppendFormat("'{0}': {{", type.Name);
                            jsIndent++;
                            foreach (var method in rootMethodsByType[type])
                            {
                                jsNewLine();
                                js.AppendFormat("'{0}': {1},", method.Name, methodNames[method]);
                            }
                            js.Length--;
                            jsIndent--;
                            jsNewLine();
                            js.Append("},");
                        }
                        js.Length--;
                    }
                    jsIndent--;
                    jsNewLine();
                    js.Append("}");
                };
                var trees = NamespaceTree.Make(rootMethodsByType.Select(x => x.Key));
                foreach (var tree in trees)
                {
                    jsNewLine();
                    js.AppendFormat("window['{0}'] = ", tree.NamespacePart);
                    treeToJs(tree);
                    js.Append(";");
                }
            }
            else
            {
                jsNewLine();
                js.AppendFormat("window['main'] = {0};", methodNames[rootMethods.First()]);
            }

            jsIndent--;
            jsNewLine();
            jsNewLine();
            js.Append("})();");

            var jsStr = js.ToString();
            //Console.WriteLine(jsStr);
            var qFieldMap =
                from fieldName in fieldNames
                let declType = fieldName.Key.DeclaringType
                               let declTypeMapped = JsResolver.TypeMapReverse(declType) ?? declType
                                                    let declTypeName = declTypeMapped.AssemblyQualifiedName()
                                                                       where declType != null
                                                                       group fieldName by declTypeName;
            var fieldMap = qFieldMap.ToDictionary(x => x.Key, x => x.ToDictionary(y => y.Key.Name, y => y.Value));
            var qTypeMap =
                from typeName in typeNames
                let type = typeName.Key.AssemblyQualifiedName()
                           where type != null
                           select new { type, typeName.Value };
            var ttt       = qTypeMap.ToArray();
            var typeMap   = ttt.ToDictionary(x => x.type, x => x.Value);
            var jsTypeMap = new JsonTypeMap(typeMap, fieldMap);

            return(new JsResult(jsStr, jsTypeMap));
        }
Пример #13
0
 protected override ICode VisitJsExplicit(StmtJsExplicit s)
 {
     this.NewLine();
     this.HandleExplicitJs(s.JavaScript, s.NamedExprs);
     return(s);
 }
Пример #14
0
 public Stmt GetImpl(Ctx ctx) {
     var a = ctx.MethodParameter(0, "a");
     var stmt = new StmtJsExplicit(ctx, "return [(~a[0]) >>> 0, (~a[1]) >>> 0];", a);
     return stmt;
 }
Пример #15
0
 public Stmt GetImpl(Ctx ctx) {
     var a = ctx.MethodParameter(0, "a");
     var b = ctx.MethodParameter(1, "b");
     var stmt = new StmtJsExplicit(ctx, "return [(a[0] ^ b[0]) >>> 0, (a[1] ^ b[1]) >>> 0];", a, b);
     return stmt;
 }
Пример #16
0
 protected override ICode VisitJsExplicit(StmtJsExplicit s) {
     this.NewLine();
     this.code.Append(s.JavaScript);
     return s;
 }
Пример #17
0
            public Stmt GetImpl(Ctx ctx) {
                var a = ctx.MethodParameter(0, "a");
                var b = ctx.MethodParameter(1, "b");
                var u = ctx.Local(ctx.Int32.MakeArray(), "u");
                var v = ctx.Local(ctx.Int32.MakeArray(), "v");
                var m = ctx.Local(ctx.Int32, "m");
                var n = ctx.Local(ctx.Int32, "n");
                var r = ctx.Local(ctx.Int32.MakeArray(), "r");
                var q = ctx.Local(ctx.Int32.MakeArray(), "q");
                var i = ctx.Local(ctx.Int32, "i");
                var j = ctx.Local(ctx.Int32, "j");
                var k = ctx.Local(ctx.Int32, "k");
                var l = ctx.Local(ctx.Int32, "l");
                var p = ctx.Local(ctx.Int32, "p");
                var t = ctx.Local(ctx.Int32, "t");
                var s = ctx.Local(ctx.Int32, "s");
                var x = ctx.Local(ctx.Int32, "x");
                var qhat = ctx.Local(ctx.Int32, "qhat");
                var rhat = ctx.Local(ctx.Int32, "rhat");
                var mask = ctx.Literal(0xffff, ctx.Int32, "mask");
                var limit = ctx.Literal(0x10000, ctx.Int32, "limit");
                var divByZeroEx = new ExprNewObj(ctx, ctx.Module.Import(typeof(DivideByZeroException).GetConstructor(Type.EmptyTypes))).Named("divByZeroEx");
                var js = @"
v = [b[1] & mask, b[1] >>> 16, b[0] & mask, b[0] >>> 16];
while (v[v.length - 1] === 0) {
    v = v.slice(0, -1);
    if (v.length === 0) throw divByZeroEx;
}
n = v.length;
u = [a[1] & mask, a[1] >>> 16, a[0] & mask, a[0] >>> 16];
while (u[u.length - 1] === 0 && u.length > n) u = u.slice(0, -1);
m = u.length;
r = [0, 0, 0, 0];
q = [0, 0, 0, 0];
if (n === 1) {
    k = 0;
    for (j = m - 1; j >= 0; j--) {
        l = k * limit + u[j]
        q[j] = (l / v[0]) >>> 0;
        k = l - q[j] * v[0];
    }
    r[0] = k;
} else {
    x = v[n - 1];
    s = 16;
    while (x > 0) {
        x >>>= 1;
        s--;
    }
    l = 0;
    for (i = n - 1; i > 0; i--) v[i] = ((v[i] << s) | (v[i - 1] >> (16 - s))) & mask;
    v[0] = (v[0] << s) & mask;
    u.push(0);
    for (i = m; i> 0; i--) u[i] = ((u[i] << s) | (u[i - 1] >> (16 - s))) & mask;
    u[0] = (u[0] << s) & mask;
    for (j = m - n; j >= 0; j--) {
        l = u[j + n] * limit + u[j + n - 1];
        qhat = (l / v[n - 1]) >>> 0;
        rhat = l - qhat * v[n - 1];
        for (;;) {
            if (qhat >= limit || qhat * v[n - 2] > limit * rhat + u[j + n - 2]) {
                qhat--;
                rhat += v[n - 1];
                if (rhat < limit) continue;
            }
            break;
        }
        k = 0;
        for (i = 0; i < n; i++) {
            p = qhat * v[i];
            t = u[i + j] - k - (p & mask);
            u[i + j] = t & mask;
            k = (p >> 16) - (t >> 16);
        }
        t = u[j + n] - k;
        u[j + n] = t;
        q[j] = qhat;
        if (t < 0) {
            q[j]--;
            k = 0;
            for (i = 0; i < n; i++) {
                t = u[i + j] + n[i] + k;
                u[i + j] = t & mask;
                k = t >> 16;
            }
            u[j + n] += k;
        }
    }
    for (i = 0; i< n; i++) {
        r[i] = ((u[i] >> s) | (u[i + 1] << (16 - s))) & mask;
    }
}
return [[q[2] + q[3] * limit, q[0] + q[1] * limit], [r[2] + r[3] * limit, r[0] + r[1] * limit]];
";
                var stmt = new StmtJsExplicit(ctx, js, a, b, u, v, m, n, r, q, i, j, k, l, p, s, t, qhat, rhat, mask, limit, divByZeroEx);
                return stmt;
            }
Пример #18
0
 private static Stmt FindStmtReturn(Ctx ctx) {
     var mRef = ctx.MRef;
     var mRefRedirected = GetRedirect(mRef);
     if (mRefRedirected != null) {
         mRef = mRefRedirected;
     }
     var mDef = mRef.Resolve();
     var jsAttr = mDef.GetCustomAttribute<JsAttribute>();
     if (jsAttr != null) {
         var ctorArgs = jsAttr.ConstructorArguments;
         if (ctorArgs.Count == 2) {
             if (ctorArgs[0].Type.IsType() && ((Array)ctorArgs[1].Value).Length == 0) {
                 // IJsImpl class provides the implementation
                 var iJsImpltype = ((TypeReference)ctorArgs[0].Value).LoadType();
                 var iJsImpl = (IJsImpl)Activator.CreateInstance(iJsImpltype);
                 var stmt = iJsImpl.GetImpl(ctx);
                 return stmt;
             }
         } else if (ctorArgs.Count == 1) {
             if (ctorArgs[0].Type.IsString()) {
                 // Explicit JS string provides implementation
                 var js = (string)ctorArgs[0].Value;
                 var parameters = mRef.Parameters.Select((x, i) => ctx.MethodParameter(i, ((char)('a' + i)).ToString())).ToArray();
                 var stmt = new StmtJsExplicit(ctx, js, parameters.Concat(ctx.ThisNamed));
                 return stmt;
             }
         }
     }
     var mappedType = TypeMap(ctx.TRef);
     if (mappedType == null && mDef.GetCustomAttribute<JsRedirectAttribute>() != null) {
         mappedType = ctx.TRef;
     }
     if (mappedType != null) {
         var mappedMRef = FindJsMember(mRef, mappedType);
         if (mappedMRef != null) {
             if (mappedMRef.ReturnType.FullName == typeof(Stmt).FullName) {
                 var m = mappedMRef.LoadMethod();
                 var stmt = (Stmt)m.Invoke(null, new object[] { ctx });
                 return stmt;
             }
         }
     }
     return null;
 }
Пример #19
0
 public static Stmt ToString(Ctx ctx) {
     var js = "return String.fromCharCode(this);";
     var stmt = new StmtJsExplicit(ctx, js, ctx.ThisNamed);
     return stmt;
 }
Пример #20
0
            public Stmt GetImpl(Ctx ctx) {
                var a = ctx.MethodParameter(0, "a");
                var b = ctx.MethodParameter(1, "b");
                var neg = ctx.Local(ctx.Boolean, "neg");
                var aNegate = new ExprUnary(ctx, UnaryOp.Negate, ctx.Int64, a.Expr).Named("aNegate");
                var bNegate = new ExprUnary(ctx, UnaryOp.Negate, ctx.Int64, b.Expr).Named("bNegate");
                var divMod = new ExprCall(ctx, (Func<UInt64, UInt64, object>)_Int64UInt64.UInt64DivRem, null, a.Expr, b.Expr).Named("divMod");
                var r = ctx.Local(ctx.Int64, "r");
                var rNegate = new ExprUnary(ctx, UnaryOp.Negate, ctx.Int64, r.Expr).Named("rNegate");
                // TODO: Throw ArithmeticException if a == Int64.MinValue and b == -1
                // TODO: Handle a or b being Int64.MinValue
                var js = @"
neg = false;
if (a[0] >>> 31) {
    a = aNegate;
    neg = true;
}
if (b[0] >>> 31) b = bNegate;
r = divMod[1];
return neg ? rNegate : r;
";
                var stmt = new StmtJsExplicit(ctx, js, a, b, neg, r, aNegate, bNegate, divMod, rNegate);
                return stmt;
            }
Пример #21
0
 public static Stmt get_FullName(Ctx ctx) {
     var eNamespace = new ExprJsTypeData(ctx, TypeData.Namespace).Named("namespace");
     var eName = new ExprJsTypeData(ctx, TypeData.Name).Named("name");
     var stmt = new StmtJsExplicit(ctx, "return this.namespace+\".\"+this.name;", ctx.ThisNamed, eNamespace, eName);
     return stmt;
 }
Пример #22
0
        public static JsResult CreateFrom(IEnumerable<MethodReference> rootMethods, bool verbose = false, bool testing = false) {
            var todo = new Queue<MethodReference>();
            foreach (var method in rootMethods) {
                todo.Enqueue(method);
            }
            Action<MethodReference> addTodo = m => {
                if (m.ContainsGenericParameters()) {
                    throw new Exception("Cannot add todo method with generic parameters");
                }
                todo.Enqueue(m);
            };
            // Each method, with the count of how often it is referenced.
            var methodsSeen = new Dictionary<MethodReference, int>(rootMethods.ToDictionary(x => x, x => 1), TypeExtensions.MethodRefEqComparerInstance);
            // Each type, with the count of how often it is referenced directly (newobj only at the moment).
            var typesSeen = new Dictionary<TypeReference, int>(TypeExtensions.TypeRefEqComparerInstance);
            // ASTs of all methods
            var methodAsts = new Dictionary<MethodReference, ICode>(TypeExtensions.MethodRefEqComparerInstance);
            // Each field, with the count of how often it is referenced.
            var fieldAccesses = new Dictionary<FieldReference, int>(TypeExtensions.FieldReqEqComparerInstance);
            // Each type records which virtual methods have their NewSlot definitions
            var virtualCalls = new Dictionary<TypeReference, HashSet<MethodReference>>(TypeExtensions.TypeRefEqComparerInstance);
            // Each basemost virtual method records the least-derived type actually called
            // This allows only virtual methods in more-derived types to be transcoded
            var virtualCallExactMethods = new Dictionary<MethodReference, IEnumerable<TypeReference>>(TypeExtensions.MethodRefEqComparerInstance);
            // Each interface type records which interface methods are called
            var interfaceCalls = new Dictionary<TypeReference, HashSet<MethodReference>>(TypeExtensions.TypeRefEqComparerInstance);
            // All instance constructors must be updated after all methods have been processed, to initialise all referenced
            // instance fields in the type, and to sort out 'return' statements.
            // This has to be done later, so the list of referenced fields in complete
            var instanceConstructors = new List<Ctx>();

            while (todo.Any()) {
                // TODO: parallelise
                var mRef = todo.Dequeue();
                var mDef = mRef.Resolve();
                var tRef = mRef.DeclaringType;
                var tDef = tRef.Resolve();
                var ctx = new Ctx(tRef, mRef);
                var ast = (ICode)JsResolver.ResolveMethod(ctx);
                if (ast == null) {
                    var transcodeCtx = JsResolver.TranslateCtx(ctx) ?? ctx;
                    if (transcodeCtx.MRef.ContainsGenericParameters()) {
                        throw new InvalidOperationException("Method/type must not have generic parameters");
                    }
                    if (transcodeCtx.MDef.IsAbstract) {
                        throw new InvalidOperationException("Cannot transcode an abstract method");
                    }
                    if (transcodeCtx.MDef.IsInternalCall) {
                        throw new InvalidOperationException("Cannot transcode an internal call");
                    }
                    if (transcodeCtx.MDef.IsExternal()) {
                        throw new InvalidOperationException("Cannot transcode an external method");
                    }
                    if (!transcodeCtx.MDef.HasBody) {
                        throw new InvalidOperationException("Cannot transcode method without body");
                    }

                    if (!typesSeen.ContainsKey(tRef)) {
                        typesSeen.Add(tRef, 0);
                    }
                    ast = Transcoder.ToAst(transcodeCtx, verbose);
                }

                for (int i = 0; ; i++) {
                    var astOrg = ast;
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsRewriteSealedVCalls.V(s), (Stmt)ast, "VisitorJsRewriteSealedVCalls", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveAll.V(s), (Stmt)ast, "VisitorJsResolveAll", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveConv.V(s), (Stmt)ast, "VisitorJsResolveConv", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveSpecialTypes.V(s), (Stmt)ast, "VisitorJsResolveSpecialTypes", verbose);
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveDelegates.V(s), (Stmt)ast, "VisitorJsResolveDelegates", verbose);
                    // 64bit must be after everything else
                    ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolve64Bit.V(s), (Stmt)ast, "VisitorJsResolve64Bit", verbose);
                    if (ast == astOrg) {
                        break;
                    }
                    if (i > 10) {
                        // After 10 iterations even the most complex method should be sorted out
                        throw new InvalidOperationException("Error: Stuck in loop trying to resolve AST");
                    }
                }
                ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveValueTypes.V(s), (Stmt)ast, "VisitorJsResolveValueTypes", verbose);
                ast = Transcoder.DoStep(s => (Stmt)VisitorIfSimplification.V(s), (Stmt)ast, "VisitorIfSimplification", verbose);
                ast = Transcoder.DoStep(s => (Stmt)VisitorJsResolveByRefParameters.V(s), (Stmt)ast, "VisitorJsResolveByRefParameters", verbose);

                if (mDef.IsVirtual && mRef.DeclaringType.IsValueType) {
                    // 'this' may be boxed or unboxed. Must be unboxed if boxed
                    // This is required because in real .NET the boxed and unboxed versions are both directly
                    // available at the this reference; this is not the case in the JS emulation of boxing
                    var unbox = new StmtJsExplicit(ctx, "if (this._) this = this.v;", ctx.ThisNamed);
                    ast = new StmtBlock(ctx, unbox, (Stmt)ast);
                    Transcoder.Print((Stmt)ast, "Unbox-this", verbose);
                }

                if (mDef.IsConstructor && !mDef.IsStatic) {
                    // Instance constructor; add instance field initialisation and final return of 'this' later
                    instanceConstructors.Add(ctx);
                }

                var cctors = VisitorFindStaticConstructors.V(ast)
                    .Where(x => !TypeExtensions.MethodRefEqComparerInstance.Equals(x, mRef))
                    .Distinct(TypeExtensions.MethodRefEqComparerInstance)
                    .ToArray();
                if (cctors.Any()) {
                    // All methods that access static fields or methods must call the static constructor at the very
                    // start of the method. Except the static construtor itself, which must not recurse into itself.
                    var cctorCalls = cctors
                        .Select(x => new StmtWrapExpr(ctx, new ExprCall(ctx, x, null, Enumerable.Empty<Expr>(), false))).ToArray();
                    ast = new StmtBlock(ctx, cctorCalls.Concat((Stmt)ast));
                    Transcoder.Print((Stmt)ast, "Call-cctors", verbose);
                }

                if (mDef.IsConstructor && mDef.IsStatic) {
                    // At the beginning of the static constructor, it rewrites itself as an empty function, so it is only called once.
                    var rewrite = new StmtAssignment(ctx, new ExprJsVarMethodReference(ctx, mRef), new ExprJsEmptyFunction(ctx));
                    ast = new StmtBlock(ctx, rewrite, (Stmt)ast);
                    Transcoder.Print((Stmt)ast, "cctor-once-only", verbose);
                }

                methodAsts.Add(mRef, ast);

                var fieldRefs = VisitorFindFieldAccesses.V(ast);
                foreach (var fieldRef in fieldRefs) {
                    fieldAccesses[fieldRef] = fieldAccesses.ValueOrDefault(fieldRef) + 1;
                }
                var arrayRefs = VisitorFindNewArrays.V(ast);
                foreach (var arrayRef in arrayRefs) {
                    typesSeen[arrayRef] = typesSeen.ValueOrDefault(arrayRef) + 1;
                }
                var types = VisitorFindRequiredTypes.V(ast);
                foreach (var type in types) {
                    typesSeen[type] = typesSeen.ValueOrDefault(type) + 1;
                }
                if (mDef.GetCustomAttribute<JsReturnTypeDeepUseAttribute>(true) != null) {
                    var retType = mDef.ReturnType.FullResolve(ctx);
                    var retTypes = retType.EnumThisAllContainedTypes().ToArray();
                    foreach (var type in retTypes) {
                        typesSeen[type] = typesSeen.ValueOrDefault(type) + 1;
                        if (type.IsGenericInstance && type.Resolve().FullName == "System.Collections.Generic.Dictionary`2") {
                            // HACK - ensure no-arg ctor is present. JSON needs it
                            var ctor = type.EnumResolvedMethods().First(x => x.Name == ".ctor" && !x.HasParameters);
                            if (!methodsSeen.ContainsKey(ctor)) {
                                methodsSeen.Add(ctor, 1);
                                addTodo(ctor);
                            }
                            // HACK - ensure Add(key, value) method present. JSON need sit
                            var mAdd = type.EnumResolvedMethods().First(x => x.Name == "Add");
                            if (!methodsSeen.ContainsKey(mAdd)) {
                                methodsSeen.Add(mAdd, 1);
                                addTodo(mAdd);
                            }
                        }
                    }
                }

                var calledMethods = new List<ICall>();
                var calls = VisitorFindCalls.V(ast);
                foreach (var call in calls.Where(x => x.ExprType == Expr.NodeType.NewObj || x.IsVirtualCall)) {
                    // Add reference to each type constructed (direct access to type variable)
                    typesSeen[call.Type] = typesSeen.ValueOrDefault(call.Type) + 1;
                }
                foreach (var call in calls) {
                    if (call.CallMethod.DeclaringType.Resolve().IsInterface) {
                        var methodSet = interfaceCalls.ValueOrDefault(call.CallMethod.DeclaringType, () => new HashSet<MethodReference>(TypeExtensions.MethodRefEqComparerInstance), true);
                        methodSet.Add(call.CallMethod);
                        // Methods that require transcoding are added to 'todo' later
                        continue;
                    }
                    if (call.IsVirtualCall) {
                        var mBasemost = call.CallMethod.GetBasemostMethod(null);
                        var methodSet = virtualCalls.ValueOrDefault(mBasemost.DeclaringType, () => new HashSet<MethodReference>(TypeExtensions.MethodRefEqComparerInstance), true);
                        methodSet.Add(mBasemost);
                        var objType = call.Obj.Type;
                        var already = virtualCallExactMethods.ValueOrDefault(mBasemost).EmptyIfNull();
                        if (!already.Any(x => x.IsBaseOfOrEqual(objType))) {
                            virtualCallExactMethods[mBasemost] = already.Concat(objType).ToArray();
                        }
                        // Methods that require transcoding are added to 'todo' later
                        continue;
                    }
                    calledMethods.Add(call);
                }
                foreach (var call in calledMethods) {
                    if (methodsSeen.ContainsKey(call.CallMethod)) {
                        methodsSeen[call.CallMethod]++;
                    } else {
                        methodsSeen.Add(call.CallMethod, 1);
                        //todo.Enqueue(call.CallMethod);
                        addTodo(call.CallMethod);
                    }
                }

                if (!todo.Any()) {
                    // Add System.RuntimeType if any types have been seen
                    if (typesSeen.Any(x => x.Value > 0) && !typesSeen.Any(x => x.Key.FullName == "System.RuntimeType")) {
                        var runtimeType = ctx.Module.Import(Type.GetType("System.RuntimeType"));
                        typesSeen.Add(runtimeType, 1);
                    }
                    // Scan all virtual calls and add any required methods
                    // Need care to handle virtual methods with generic arguments
                    var virtualRoots = new HashSet<MethodReference>(virtualCalls.SelectMany(x => x.Value), TypeExtensions.MethodRefEqComparerInstance);
                    var requireMethods =
                        from type in typesSeen.Keys
                        //let typeReverseMapped = JsResolver.ReverseTypeMap(type)
                        let typeAndBases = type.EnumThisAllBaseTypes().ToArray()
                        let mVRoots = typeAndBases.SelectMany(x => virtualCalls.ValueOrDefault(x).EmptyIfNull()).ToArray()
                        let methods = type.EnumResolvedMethods(mVRoots).ToArray()
                        from method in methods//.Select(x => JsResolver.ResolveMethod(x))
                        let methodDef = method.Resolve()
                        where methodDef != null // HACK?
                        where !methodDef.IsStatic && methodDef.IsVirtual && !methodDef.IsAbstract
                        where !methodsSeen.ContainsKey(method)
                        let mBasemost = method.GetBasemostMethod(method)
                        where virtualCallExactMethods.ValueOrDefault(mBasemost).EmptyIfNull().Any(x => {
                            //return x.IsBaseOfOrEqual(typeReverseMapped) || typeReverseMapped.IsBaseOfOrEqual(x);
                            return x.IsBaseOfOrEqual(type) || type.IsBaseOfOrEqual(x);
                        })
                        where virtualRoots.Contains(mBasemost)
                        select method;
                    var requireMethodsArray = requireMethods.Distinct(TypeExtensions.MethodRefEqComparerInstance).ToArray();
                    foreach (var method in requireMethodsArray) {
                        methodsSeen.Add(method, 1); // TODO: How to properly handle count?
                        //todo.Enqueue(method);
                        addTodo(method);
                    }
                    // Scan all interface calls and add any required methods
                    var iFaceMethods =
                        from type in typesSeen.Keys
                        from iFace in interfaceCalls
                        let iFaceType = iFace.Key
                        let typeAndBases = type.EnumThisAllBaseTypes().ToArray()
                        where typeAndBases.Any(x => x.DoesImplement(iFaceType))
                        let methods = typeAndBases.SelectMany(x => x.EnumResolvedMethods(iFace.Value)).ToArray()
                        from method in methods//.Select(x => JsResolver.ResolveMethod(x))
                        where !methodsSeen.ContainsKey(method)
                        let methodDef = method.Resolve()
                        where methodDef != null // HACK?
                        where !methodDef.IsStatic && methodDef.IsVirtual && !methodDef.IsAbstract
                        from iFaceMethod in iFace.Value
                        where method.IsImplementationOf(iFaceMethod)
                        select method;
                    var iFaceMethodsArray = iFaceMethods.Distinct(TypeExtensions.MethodRefEqComparerInstance).ToArray();
                    foreach (var method in iFaceMethodsArray) {
                        methodsSeen.Add(method, 1);
                        addTodo(method);
                        //todo.Enqueue(method);
                    }
                }
            }

            // Add type count to System.RuntimeType
            var runtimeTypeInc = typesSeen.Keys.FirstOrDefault(x => x.FullName == "System.RuntimeType");
            if (runtimeTypeInc != null) {
                typesSeen[runtimeTypeInc] += 2;
            }

            var instanceFieldsByType = fieldAccesses
                .Where(x => !x.Key.Resolve().IsStatic)
                .ToLookup(x => x.Key.DeclaringType.FullResolve(x.Key), TypeExtensions.TypeRefEqComparerInstance);
            // Update all instance constructors to initialise instance fields, add final 'return' statement,
            // and update any early returns to return 'this'
            foreach (var ctx in instanceConstructors) {
                var fields = instanceFieldsByType[ctx.TRef].Select(x => x.Key);
                var initStmts = fields.Select(x => {
                    var f = x.FullResolve(ctx.TRef, ctx.MRef);
                    var assign = new StmtAssignment(ctx,
                        new ExprFieldAccess(ctx, ctx.This, f),
                        new ExprDefaultValue(ctx, f.FieldType));
                    return assign;
                }).ToArray();
                var returnStmt = new StmtReturn(ctx, ctx.This);
                var ast = methodAsts[ctx.MRef];
                ast = new StmtBlock(ctx, initStmts.Concat((Stmt)ast).Concat(returnStmt));
                ast = VisitorJsReturnThis.V(ast, ctx.This);
                methodAsts[ctx.MRef] = ast;
            }

            // Locally name all instance fields; base type names must not be re-used in derived types
            var instanceFieldsIndexed = new Dictionary<int, Tuple<IEnumerable<FieldReference>, int>>(); // <index, Tuple<all fields, total use count>>
            instanceFieldsByType.TypeTreeTraverse(x => x.Key, (fields, idx) => {
                var ordered = fields.OrderByDescending(x => x.Value).ToArray(); // Order by usage count, highest first
                foreach (var field in ordered) {
                    var idxInfo = instanceFieldsIndexed.ValueOrDefault(idx, () => Tuple.Create(Enumerable.Empty<FieldReference>(), 0));
                    var newIdxInfo = Tuple.Create((IEnumerable<FieldReference>)idxInfo.Item1.Concat(field.Key).ToArray(), idxInfo.Item2 + field.Value);
                    instanceFieldsIndexed[idx] = newIdxInfo;
                    idx++;
                }
                return idx;
            }, 0);
            var orderedInstanceFields = instanceFieldsIndexed.OrderByDescending(x => x.Value.Item2).ToArray();
            var instanceFieldNameGen = new NameGenerator();
            var instanceFieldNames = orderedInstanceFields
                .Select(x => new { name = instanceFieldNameGen.GetNewName(), fields = x.Value.Item1 })
                .SelectMany(x => x.fields.Select(y => new { f = y, name = x.name }))
                .ToArray();
            // Prepare list of static fields for global naming
            var staticFields = fieldAccesses.Where(x => x.Key.Resolve().IsStatic).ToArray();

            // Prepare local variables for global naming.
            // All locals in all methods are sorted by usage count, then all methods usage counts are combined
            var clusters = methodAsts.Values.SelectMany(x => VisitorJsPhiClusters.V(x).Select(y => new ExprVarCluster(y))).ToArray();
            var varToCluster = clusters.SelectMany(x => x.Vars.Select(y => new { cluster = x, var = y })).ToDictionary(x => x.var, x => x.cluster);
            var varsWithCount = methodAsts.Values.Select(x => {
                var methodVars = VisitorFindVars.V(x);
                // Parameters need one extra count, as they appear in the method declaration
                methodVars = methodVars.Concat(methodVars.Where(y => y.ExprType == Expr.NodeType.VarParameter).Distinct());
                var ret = methodVars.Select(y => varToCluster.ValueOrDefault(y) ?? y)
                    .GroupBy(y => y)
                    .Select(y => new { var = y.Key, count = y.Count() })
                    .OrderByDescending(y => y.count)
                    .ToArray();
                return ret;
            }).ToArray();
            var localVarCounts = new Dictionary<int, int>();
            foreach (var x in varsWithCount) {
                for (int i = 0; i < x.Length; i++) {
                    localVarCounts[i] = localVarCounts.ValueOrDefault(i) + x[i].count;
                }
            }

            // Globally name all items that require names
            var needNaming =
                localVarCounts.Select(x => new { item = (object)x.Key, count = x.Value })
                .Concat(methodsSeen.Select(x => new { item = (object)x.Key, count = x.Value }))
                .Concat(staticFields.Select(x => new { item = (object)x.Key, count = x.Value }))
                .Concat(typesSeen.Select(x => new { item = (object)x.Key, count = x.Value }))
                .Where(x => x.count > 0)
                .OrderByDescending(x => x.count)
                .ToArray();
            var nameGen = new NameGenerator();
            var globalNames = needNaming.ToDictionary(x => x.item, x => nameGen.GetNewName());

            // Create map of all local variables to their names
            var localVarNames = varsWithCount.Select(x => x.Select((y, i) => new { y.var, name = globalNames[i] }))
                .SelectMany(x => x)
                .SelectMany(x => {
                    var varCluster = x.var as ExprVarCluster;
                    if (varCluster != null) {
                        return varCluster.Vars.Select(y => new { var = y, name = x.name });
                    } else {
                        return new[] { x };
                    }
                })
                .ToDictionary(x => x.var, x => x.name);

            // Create map of all method names
            var methodNames = methodsSeen.Keys.ToDictionary(x => x, x => globalNames[x], TypeExtensions.MethodRefEqComparerInstance);

            // Create list of all static field names
            var staticFieldNames = staticFields.Select(x => new { f = x.Key, name = globalNames[x.Key] });
            // Create map of all fields
            if (testing) {
                instanceFieldNames = instanceFieldNames.Select(x => {
                    switch (x.f.FullName) {
                    case "System.String System.Exception::_message":
                        return new { x.f, name = "$$_message" };
                    }
                    return x;
                }).ToArray();
            }
            var fieldNames = instanceFieldNames.Concat(staticFieldNames).ToDictionary(x => x.f, x => x.name, TypeExtensions.FieldReqEqComparerInstance);
            // Create map of type names
            var typeNames = typesSeen
                .Where(x => x.Value > 0)
                .ToDictionary(x => x.Key, x => globalNames[x.Key], TypeExtensions.TypeRefEqComparerInstance);

            // Create virtual call tables
            var virtualCallIndices = new Dictionary<MethodReference, int>(TypeExtensions.MethodRefEqComparerInstance);
            var allVirtualMethods = new Dictionary<TypeReference, HashSet<MethodReference>>(TypeExtensions.TypeRefEqComparerInstance);
            typesSeen.Select(x => x.Key).TypeTreeTraverse(x => x, (type, vCalls) => {
                var mNewSlots = virtualCalls.ValueOrDefault(type).EmptyIfNull().ToArray();
                int idx = vCalls.Length;
                foreach (var mNewSlot in mNewSlots) {
                    virtualCallIndices[mNewSlot] = idx++;
                }
                var vCallsWithThisType = vCalls.Concat(mNewSlots).ToArray();
                if (vCallsWithThisType.Length > 0) {
                    var typesAndBases = type.EnumThisAllBaseTypes().ToArray();
                    var mVRoots = typesAndBases.SelectMany(x => virtualCalls.ValueOrDefault(x).EmptyIfNull()).ToArray();
                    var ms = type.EnumResolvedMethods(mVRoots).ToArray();
                    for (int i = 0; i < vCalls.Length; i++) {
                        var mVCall = vCallsWithThisType[i];
                        foreach (var m in ms) {
                            if (m.MatchMethodOnly(mVCall)) {
                                vCallsWithThisType[i] = m;
                            }
                        }
                    }
                    var typeVMethods = new HashSet<MethodReference>(vCallsWithThisType, TypeExtensions.MethodRefEqComparerInstance);
                    allVirtualMethods.Add(type, typeVMethods);
                }
                return vCallsWithThisType;
            }, new MethodReference[0]);

            var typeData = Enum.GetValues(typeof(TypeData)).Cast<TypeData>().ToArray();

            // Name all items that are within the type information
            var needTypeInformationNaming =
                interfaceCalls.Select(x => new { item = (object)x.Key, count = 1 })
                .Concat(typeData.Select(x => new { item = (object)x, count = 1 }))
                .OrderByDescending(x => x.count)
                .ToArray();
            var typeInformationNameGen = new NameGenerator();
            var typeInformationNames = needTypeInformationNaming.ToDictionary(x => x.item, x => typeInformationNameGen.GetNewName());
            if (testing) {
                typeInformationNames[TypeData.Name] = "$$TypeName";
                typeInformationNames[TypeData.Namespace] = "$$TypeNamespace";
            }

            // Create map of interfaces to their names
            var interfaceNames = interfaceCalls.Keys.ToDictionary(x => x, x => typeInformationNames[x], TypeExtensions.TypeRefEqComparerInstance);
            var interfaceCallIndices = interfaceCalls.SelectMany(x => x.Value.Select((m, i) => new { m, i })).ToDictionary(x => x.m, x => x.i, TypeExtensions.MethodRefEqComparerInstance);

            // Create map of type data constants
            var typeDataNames = typeData.ToDictionary(x => x, x => typeInformationNames[x]);

            var resolver = new JsMethod.Resolver {
                LocalVarNames = localVarNames,
                MethodNames = methodNames,
                FieldNames = fieldNames,
                TypeNames = typeNames,
                VirtualCallIndices = virtualCallIndices,
                InterfaceCallIndices = interfaceCallIndices,
                InterfaceNames = interfaceNames,
                TypeDataNames = typeDataNames,
            };

            var js = new StringBuilder();
            js.Append("(function(){");
            int jsIndent = 1;
            Action jsNewLine = () => {
                js.AppendLine();
                js.Append(' ', jsIndent * JsMethod.tabSize);
            };

            jsNewLine();
            js.Append("\"use strict\";");
            jsNewLine();
            // Construct methods
            foreach (var methodInfo in methodAsts) {
                var mRef = methodInfo.Key;
                var ast = methodInfo.Value;
                var mJs = JsMethod.Create(mRef, resolver, ast);
                var mJsLines = mJs.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
                foreach (var line in mJsLines) {
                    jsNewLine();
                    js.Append(line);
                }
            }

            // Construct static fields
            foreach (var field in staticFields.Select(x => x.Key)) {
                jsNewLine();
                js.AppendFormat("// {0}", field.FullName);
                jsNewLine();
                if (field.Name == "Empty" && field.DeclaringType.FullName == "System.String") {
                    // Special case, as string does not have a static constructor to set String.Empty
                    js.AppendFormat("var {0} = \"\";", fieldNames[field]);
                } else {
                    js.AppendFormat("var {0} = {1};", fieldNames[field], DefaultValuer.Get(field.FieldType, fieldNames));
                }
            }

            // Construct type data
            var typesSeenOrdered = typesSeen
                .Where(x => x.Value > 0)
                .Select(x => x.Key)
                .OrderByReferencedFirst(x => x)
                .ToArray();
            var domTypes = new Dictionary<string, TypeReference>();
            foreach (var type in typesSeenOrdered) {
                var unmappedType = type;
                var tDef = unmappedType.Resolve();
                // Check for DOM types
                var jsClassAttr = tDef.GetCustomAttribute<JsClassAttribute>();
                if (jsClassAttr != null) {
                    if (jsClassAttr.ConstructorArguments.Count == 1) {
                        // Non-abstract types only
                        var tagOrConstructorName = (string)jsClassAttr.ConstructorArguments[0].Value;
                        domTypes.Add(tagOrConstructorName, unmappedType);
                    }
                }
                // Type JS
                jsNewLine();
                js.AppendFormat("// {0}", unmappedType.FullName);
                jsNewLine();
                js.AppendFormat("var {0}={{", typeNames[type]);
                // Type information
                js.AppendFormat("{0}:\"{1}\"", typeDataNames[TypeData.JsName], typeNames[type]);
                js.AppendFormat(", {0}:\"{1}\"", typeDataNames[TypeData.Name], unmappedType.Name());
                js.AppendFormat(", {0}:\"{1}\"", typeDataNames[TypeData.Namespace], unmappedType.Namespace);
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsValueType], unmappedType.IsValueType ? "true" : "false");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsPrimitive], unmappedType.IsPrimitive ? "true" : "false");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsArray], unmappedType.IsArray ? "true" : "false");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.ElementType], unmappedType.IsArray ? typeNames.ValueOrDefault(((ArrayType)unmappedType).ElementType, "null") : "null");
                js.AppendFormat(", {0}:{1}", typeDataNames[TypeData.IsInterface], tDef.IsInterface ? "true" : "false");
                var assignableTo = typesSeenOrdered.Where(x => unmappedType.IsAssignableTo(x)).Where(x => !x.IsSame(unmappedType)).ToArray();
                js.AppendFormat(", {0}:[{1}]", typeDataNames[TypeData.AssignableTo], string.Join(", ", assignableTo.Select(x => typeNames[x])));
                if (tDef.FullName == "System.Collections.Generic.Dictionary`2") {
                    var typeGen = (GenericInstanceType)type;
                    var dict = tDef.Module.Import(typeof(DotNetWebToolkit.Cil2Js.JsResolvers.Classes._Dictionary<,>));
                    var dictGen = dict.MakeGeneric(typeGen.GenericArguments[0], typeGen.GenericArguments[1]);
                    var jsSlotsName = fieldNames[dictGen.EnumResolvedFields().First(x => x.Name == "slots")];
                    var slot = dictGen.Resolve().NestedTypes.First(x => x.Name == "Slot");
                    var slotGen = slot.MakeGeneric(typeGen.GenericArguments[0], typeGen.GenericArguments[1]);
                    var jsHashCodeName = fieldNames[slotGen.EnumResolvedFields().First(x => x.Name == "hashCode")];
                    var jskeyName = fieldNames[slotGen.EnumResolvedFields().First(x => x.Name == "key")];
                    var jsValueName = fieldNames[slotGen.EnumResolvedFields().First(x => x.Name == "value")];
                    js.AppendFormat(", {0}:['{1}','{2}','{3}','{4}']", typeDataNames[TypeData.IsDictionary], jsSlotsName, jsHashCodeName, jskeyName, jsValueName);
                }
                if (!tDef.IsInterface) {
                    if (!tDef.IsAbstract) {
                        // Virtual method table, only needed on concrete types
                        var typeAndBases = type.EnumThisAllBaseTypes().ToArray();
                        var methods = allVirtualMethods.ValueOrDefault(type);
                        if (methods != null) {
                            var idxs = methods
                                .Select(x => {
                                    var xBasemost = x.GetBasemostMethod(x);
                                    return new { m = x, idx = virtualCallIndices[xBasemost] };
                                })
                                .OrderBy(x => x.idx)
                                .ToArray();
                            var s = string.Join(", ", idxs.Select(x => methodNames.ValueOrDefault(x.m, "null")));
                            js.AppendFormat(", {0}:[{1}]", typeDataNames[TypeData.VTable], s);
                        }
                        // Interface tables, only needed on concrete types
                        var implementedIFaces = interfaceCalls.Where(x => typeAndBases.Any(y => y.DoesImplement(x.Key))).ToArray();
                        foreach (var iFace in implementedIFaces) {
                            js.Append(", ");
                            var iFaceName = interfaceNames[iFace.Key];
                            js.AppendFormat("{0}:[", iFaceName);
                            var qInterfaceTableNames =
                                from iMethod in iFace.Value
                                let tMethod = typeAndBases.SelectMany(x => x.EnumResolvedMethods(iMethod)).First(x => x.IsImplementationOf(iMethod))
                                //let tM2 = JsResolver.ResolveMethod(tMethod)
                                let idx = interfaceCallIndices[iMethod]
                                orderby idx
                                let methodName = methodNames[tMethod]
                                select methodName;
                            var interfaceTableNames = qInterfaceTableNames.ToArray();
                            js.Append(string.Join(", ", interfaceTableNames));
                            js.Append("]");
                        }
                    }
                }
                if (tDef.IsEnum && tDef.GetCustomAttribute<JsStringEnumAttribute>() != null) {
                    // JS string/enum map
                    var values = tDef.Fields.Where(x => x.IsLiteral).Select(x => {
                        return string.Format("{0}:\"{1}\",\"{1}\":{0}", x.Constant, JsResolver.JsName(x));
                    }).ToArray();
                    js.AppendFormat(", {0}:{{{1}}}", typeDataNames[TypeData.EnumStringMap], string.Join(", ", values));
                }
                // end
                js.Append("};");
            }
            // Add type of each type, if System.RuntimeType has been seen
            var typeRuntimeType = typesSeen.Keys.FirstOrDefault(x => x.FullName == "System.RuntimeType");
            if (typeRuntimeType != null) {
                jsNewLine();
                foreach (var type in typesSeenOrdered) {
                    js.Append(typeNames[type]);
                    js.Append("._ = ");
                }
                js.Append(typeNames[typeRuntimeType]);
                js.Append(";");
            }
            // Add comments descibing each interface
            jsNewLine();
            js.Append("// Interface name map");
            jsNewLine();
            js.AppendFormat("// {0} = VTable", typeDataNames[TypeData.VTable]);
            foreach (var iFace in interfaceNames) {
                jsNewLine();
                js.AppendFormat("// {0} = {1}", iFace.Value, iFace.Key.FullName);
            }
            // Add map of DOM types
            if (domTypes.Any()) {
                jsNewLine();
                js.Append("// DOM type mapping");
                jsNewLine();
                // TODO: Auto-name this
                js.Append("var __ = {");
                jsIndent++;
                foreach (var domType in domTypes) {
                    jsNewLine();
                    js.AppendFormat("'{0}': {1},", domType.Key, typeNames[domType.Value]);
                }
                js.Length--;
                jsIndent--;
                jsNewLine();
                js.Append("};");
            }
            if (typesSeenOrdered.Any()) {
                jsNewLine();
                js.Append("// json type mapping");
                jsNewLine();
                // TODO: Auto-name this
                js.Append("var $$ = {");
                foreach (var type in typesSeenOrdered) {
                    js.AppendFormat("'{0}':{0},", typeNames[type]);
                }
                js.Length--;
                js.Append("};");
                var typesDicts = typesSeenOrdered
                    .Where(x => x.IsGenericInstance && x.Resolve().FullName == "System.Collections.Generic.Dictionary`2")
                    .ToArray();
                if (typesDicts.Any()) {
                    jsNewLine();
                    js.Append("// json dictionary info");
                    jsNewLine();
                    // TODO: Auto-name or get rid of this
                    js.Append("var $d = {");
                    var any = false;
                    foreach (var type in typesDicts) {
                        var typeName = typeNames[type];
                        var ctor = type.EnumResolvedMethods().First(x => x.Name == ".ctor" && !x.HasParameters);
                        var mAdd = type.EnumResolvedMethods().First(x => x.Name == "Add");
                        // If dict not involved in JSON, these methods may not be present
                        if (methodNames.ContainsKey(ctor) && methodNames.ContainsKey(mAdd)) {
                            var ctorName = methodNames[ctor];
                            var mAddName = methodNames[mAdd];
                            js.AppendFormat("'{0}':[{1},{2}],", typeName, ctorName, mAddName);
                            any = true;
                        }
                    }
                    if (any) {
                        js.Length--;
                    }
                    js.Append("};");
                }
            }

            jsNewLine();
            jsNewLine();
            js.Append("// Exports");
            if (!testing) {
                var rootMethodsByType = rootMethods.ToLookup(x => x.DeclaringType, TypeExtensions.TypeRefEqComparerInstance);
                Action<NamespaceTree> treeToJs = null;
                treeToJs = tree => {
                    js.Append("{");
                    jsIndent++;
                    foreach (var subNs in tree.Namespaces) {
                        jsNewLine();
                        js.AppendFormat("'{0}': ", subNs.NamespacePart);
                        treeToJs(subNs);
                    }
                    if (tree.Types.Any()) {
                        foreach (var type in tree.Types) {
                            jsNewLine();
                            js.AppendFormat("'{0}': {{", type.Name);
                            jsIndent++;
                            foreach (var method in rootMethodsByType[type]) {
                                jsNewLine();
                                js.AppendFormat("'{0}': {1},", method.Name, methodNames[method]);
                            }
                            js.Length--;
                            jsIndent--;
                            jsNewLine();
                            js.Append("},");
                        }
                        js.Length--;
                    }
                    jsIndent--;
                    jsNewLine();
                    js.Append("}");
                };
                var trees = NamespaceTree.Make(rootMethodsByType.Select(x => x.Key));
                foreach (var tree in trees) {
                    jsNewLine();
                    js.AppendFormat("window['{0}'] = ", tree.NamespacePart);
                    treeToJs(tree);
                    js.Append(";");
                }
            } else {
                jsNewLine();
                js.AppendFormat("window['main'] = {0};", methodNames[rootMethods.First()]);
            }

            jsIndent--;
            jsNewLine();
            jsNewLine();
            js.Append("})();");

            var jsStr = js.ToString();
            //Console.WriteLine(jsStr);
            var qFieldMap =
                from fieldName in fieldNames
                let declType = fieldName.Key.DeclaringType
                let declTypeMapped = JsResolver.TypeMapReverse(declType) ?? declType
                let declTypeName = declTypeMapped.AssemblyQualifiedName()
                where declType != null
                group fieldName by declTypeName;
            var fieldMap = qFieldMap.ToDictionary(x => x.Key, x => x.ToDictionary(y => y.Key.Name, y => y.Value));
            var qTypeMap =
                from typeName in typeNames
                let type = typeName.Key.AssemblyQualifiedName()
                where type != null
                select new { type, typeName.Value };
            var ttt = qTypeMap.ToArray();
            var typeMap = ttt.ToDictionary(x => x.type, x => x.Value);
            var jsTypeMap = new JsonTypeMap(typeMap, fieldMap);
            return new JsResult(jsStr, jsTypeMap);
        }
Пример #23
0
 public Stmt GetImpl(Ctx ctx) {
     var a = ctx.MethodParameter(0, "a");
     var b = ctx.MethodParameter(1, "b");
     var stmt = new StmtJsExplicit(ctx, "return a[0] !== b[0] || a[1] !== b[1];", a, b);
     return stmt;
 }
Пример #24
0
 public static Stmt GetType(Ctx ctx) {
     var js = "return typeof(this) == \"string\" ? stringType : this._ || __[this.tagName] || __[this.constructor.name];";
     var stringType = new ExprJsTypeVarName(ctx, ctx.String).Named("stringType");
     var stmt = new StmtJsExplicit(ctx, js, ctx.ThisNamed, stringType);
     return stmt;
 }