private static IEnumerable <KeyValuePair <string, object> > Describe(IType type) { var hierarchy = new JsObject(type.GetFullTypeHierarchy().Select(x => new KeyValuePair <object, object>(x.FullName, 1))); var newFunc = new JsFunction(); newFunc.Body.Add(type.New().Return()); var list = new PropertyList { { "ns", type.Namespace ?? "" }, { "name", type.Name }, { "kind", type.GetCorlibKind() }, { "$typecode", type.JsTypeCode() }, { "$hierarchy", hierarchy }, { "$new", newFunc }, }; if (type.ElementType != null) { list.Add("$elemType", type.ElementType.FullName); } if (type.ValueType != null) { list.Add("$valueType", type.ValueType.FullName); } return(list); }
private JsNode OpNewarr(MethodContext context, IType elemType) { var key = new InstructionKey(InstructionCode.Newarr, elemType); var var = context.Vars[key]; if (var != null) { return(var.Id()); } var type = RegisterArrayType(elemType); var elemInit = new JsFunction(null); elemInit.Body.Add(elemType.InitialValue().Return()); var info = new JsObject { { "init", elemInit }, { "type", type.FullName }, { "box", new BoxingImpl(this).Box(context, elemType) }, { "unbox", new BoxingImpl(this).Unbox(context, elemType) }, { "etc", elemType.JsTypeCode() }, }; var = context.Vars.Add(key, info); CompileClass(SystemTypes.Array); CompileType(elemType); return(var.Id()); }
public static void CompileCopy(JsClass klass) { var type = klass.Type; var func = new JsFunction(null); var obj = "o".Id(); func.Body.Add(type.New().Var(obj.Value)); foreach (var field in GetInstanceFields(klass)) { var name = field.JsName(); var value = "this".Id().Get(name); if (field.Type.TypeKind == TypeKind.Struct) { value = "$copy".Id().Call(value); } func.Body.Add(obj.Set(name, value)); } func.Body.Add(obj.Return()); klass.ExtendPrototype(func, "$copy"); }
private void AddImpl(IMethod method, JsFunction func) { var type = _host.SystemTypes.Array; var klass = _host.CompileClass(type); klass.Add(new JsGeneratedMethod(method.JsFullName(type), func)); }
private void Build(JsClass klass) { var type = klass.Type; // ignore Avm.String we use System.String if (type.IsAvmString()) { return; } Register(type); var func = new JsFunction(null); if (type.Is(SystemTypeCode.Array)) { func.Body.Add("$types".Id().Get("this".Id().Get("m_type")).Call().Return()); } else { func.Body.Add("$types".Id().Get(type.FullName).Call().Return()); } klass.ExtendPrototype(func, "GetType"); }
private void Register(IType type) { var init = new JsFunction(); var prop = string.Format("$types['{0}']", "$$" + type.FullName.JsEscape()); init.Body.Add(new JsText(string.Format("var t = {0};", prop))); init.Body.Add(new JsText(string.Format("if (t != undefined) return t;"))); var t = "t".Id(); var typeType = _host.SystemTypes.Type; init.Body.Add(t.Set(typeType.New())); init.Body.Add(new JsText(string.Format("{0} = t;", prop))); var description = Describe(type); foreach (var property in description) { init.Body.Add(t.Set(property.Key, property.Value)); } init.Body.Add(t.Return()); _program.Add("$types".Id().Set(type.FullName, init)); }
private void CompileOp(IType type, BinaryOperator op) { // exclude natively supported types if (!HasOperators(type)) { return; } var method = _operators.Find(op, type, type); if (method == null) { return; } if (method.Data != null) { return; } var jsMethod = CompileMethod(method); var val = "v".Id(); var func = new JsFunction(null, val.Value); func.Body.Add(jsMethod.FullName.Id().Call("this".Id(), val).Return()); ExtendPrototype(type, func, op.JsName()); }
internal JsFunction CompileMethodBody(JsClass klass, IMethod method, IClrMethodBody body) { var blocks = body.GetAllProtectedBlocks().ToArray(); var context = new MethodContext(this, klass, method, blocks); var func = new JsFunction(null, method.JsParams()); //TODO: cache info and code as separate class property var info = new JsObject(true) { { "isVoid", method.IsVoid() }, { "blocks", CompileBlocks(blocks) }, { "blockMap", CompileBlockMap(body, blocks) }, }; var args = CompilerArgs(method); var vars = new JsArray(method.Body.LocalVariables.Select(x => x.Type.InitialValue())); var code = new JsArray(body.Code.Select <Instruction, object>(i => new JsInstruction(i, CompileInstruction(context, i))), "\n"); func.Body.Add(args.Var("args")); func.Body.Add(vars.Var("vars")); func.Body.Add(info.Var("info")); foreach (var var in context.Vars) { func.Body.Add(var); } func.Body.Add(code.Var("code")); func.Body.Add(new JsText("var ctx = new $context(info, args, vars);")); func.Body.Add(new JsText("return ctx.exec(code);")); return(func); }
private static void CompileImplementedMethods(JsClass klass, IMethod method) { if (method.IsExplicitImplementation) { return; } var impls = method.Implements; if (impls == null) { return; } if (impls.Count <= 0) { return; } var type = method.DeclaringType.JsFullName(method); var name = method.JsName(); foreach (var i in impls) { var func = new JsFunction(null, i.JsParams()); var call = "this".Id().Get(name).Call(i.JsArgs()); func.Body.Add(method.IsVoid() ? call.AsStatement() : call.Return()); klass.Add(new JsGeneratedMethod(string.Format("{0}.prototype.{1}", type, i.JsName()), func)); } }
private static void CompileGetHashCode(JsClass klass) { var func = new JsFunction(null); func.Body.Add("$hash".Id().Call("this".Id()).Return()); klass.ExtendPrototype(func, "GetHashCode"); }
public object Compile(MethodContext context, IField field) { var fieldInfo = context.Vars[field]; if (fieldInfo != null) { return(fieldInfo.Id()); } var obj = "o".Id(); var val = "v".Id(); var get = new JsFunction(null, obj.Value); var set = new JsFunction(null, obj.Value, val.Value); var info = new JsObject { { "get", get }, { "set", set } }; fieldInfo = context.Vars.Add(field, info); if (field.IsStatic) { _host.InitClass(context, get, field); _host.InitClass(context, set, field); var name = field.JsFullName(); #if JSC_FIELD_DEBUG var value = name.Id().Var("v"); get.Body.Add(value); get.Body.Add(new JsText(string.Format("if (v === undefined) throw new ReferenceError('{0} is undefined');", field.FullName))); get.Body.Add(value.Name.Id().Return()); #else get.Body.Add(name.Id().Return()); #endif set.Body.Add(name.Id().Set(val)); } else { var name = field.JsName(); #if JSC_FIELD_DEBUG var value = obj.Get(name).Var("v"); get.Body.Add(value); get.Body.Add(new JsText(string.Format("if (v === undefined) throw new ReferenceError('{0} is undefined');", field.FullName))); get.Body.Add(value.Name.Id().Return()); #else get.Body.Add(obj.Get(name).Return()); #endif set.Body.Add(obj.Set(name, val)); } return(fieldInfo.Id()); }
private static void CompileToString(JsCompiler compiler, JsClass klass) { var type = klass.Type; CompileValues(compiler, klass); var func = new JsFunction(null); var withFlags = type.HasAttribute("System.FlagsAttribute"); func.Body.Add(new JsText(withFlags ? "return $enum.flags(this);" : "return $enum.stringify(this);")); klass.ExtendPrototype(func, "toString"); }
private JsFunction CreateCallFunc(MethodContext context, IMethod method, CallInfo info) { var obj = "o".Id(); var args = "a".Id(); var func = new JsFunction(null, obj.Value, args.Value); InitClass(context, func, method); JsNode call = null; //TODO: inplace inline calls if (method.DeclaringType.Is(SystemTypeCode.Boolean) && method.IsToString()) { call = obj.Ternary("True", "False"); } //to detect stackoverflows //func.Body.Add("console.log".Id().Call(method.JsFullName()).AsStatement()); if (call == null && info != null) { if (info.ReceiverType != null && (info.Flags & CallFlags.Basecall) != 0) { call = method.JsFullName(info.ReceiverType).Id().Apply(obj, args); } else if (IsSuperCall(context, method, info.Flags)) { var baseType = context.Method.DeclaringType.BaseType; call = method.JsFullName(baseType).Id().Apply(obj, args); //TODO: remove $base if it is not needed //call = obj.Get("$base").Get(method.JsName()).Apply(obj, args); } } if (call == null) { if (!method.IsStatic && !method.IsConstructor && method.DeclaringType.IsBoxableType()) { new BoxingImpl(this).BoxUnboxed(context, method.DeclaringType, func, obj); } call = method.Apply(obj, args); } func.Body.Add(method.IsVoid() ? call.AsStatement() : call.Return()); return(func); }
private static JsFunction CompileInlineFunction(IMethod method, InlineMethodInfo info) { var parameters = method.JsParams(); var func = new JsFunction(null, parameters); switch (info.Kind) { case InlineKind.Property: //TODO: support static properties var propertyName = info.Name; if (method.IsSetter()) { func.Body.Add("this".Id().Set(propertyName, parameters[0].Id())); } else { func.Body.Add("this".Id().Get(propertyName).Return()); } break; case InlineKind.Operator: var text = "return "; for (int i = 0; i < method.Parameters.Count; i++) { if (i > 0) { text += info.Name; } text += parameters[i]; } text += ";"; func.Body.Add(new JsText(text)); break; default: var type = method.DeclaringType; //TODO: use ABC, pfx meta attributes var typeName = type.Namespace == "Avm" ? type.Name : type.JsFullName(method); var target = method.IsStatic ? typeName.Id() : "this".Id(); func.Body.Add(target.Get(info.Name).Call(parameters.Select(x => x.Id()).ToArray()).Return()); break; } return(func); }
private static void CompileEquals(JsCompiler compiler, JsClass klass) { var other = "o".Id(); var func = new JsFunction(null, other.Value); func.Body.Add(new JsText("if (o === null || o === undefined) return false;")); //TODO: check object type JsNode result = null; foreach (var field in GetInstanceFields(klass)) { var name = field.JsName(); var left = "this".Id().Get(name); var right = other.Get(name); JsNode e; // primitive and ref types if (field.Type.TypeKind != TypeKind.Struct && !field.Type.IsInt64Based()) { e = left.Op(right, BinaryOperator.Equality); } else // value types, int64 based { var objectType = compiler.SystemTypes.Object; var eq = objectType.Methods.Find("Equals", objectType, objectType); compiler.CompileMethod(eq); e = eq.JsFullName().Id().Call(left, right); } result = result == null ? e : result.And(e); } func.Body.Add(result == null ? "false".Id().Return() : result.Return()); var methodName = ObjectMethods.Find(compiler.SystemTypes.Object, ObjectMethodId.Equals).JsName(); klass.ExtendPrototype(func, methodName); }
internal void InitClass(MethodContext context, JsFunction func, ITypeMember member) { var type = member is IType ? (IType)member : member.DeclaringType; if (ExcludeInitClass(type)) { return; } if (member is IType) { CompileFields(type, true); CompileFields(type, false); } else { CompileFields(type, member.IsStatic); } new ClassInitImpl(this).InitClass(context, func, member); }
private JsNode OpNewobj(MethodContext context, IMethod method) { var key = new InstructionKey(InstructionCode.Newobj, method); var data = context.Vars[key]; if (data != null) { return(data.Id()); } var args = "a".Id(); var func = new JsFunction(null, args.Value); var info = new JsObject { { "n", method.Parameters.Count }, { "f", func }, }; data = context.Vars.Add(key, info); CompileCallMethod(method); InitClass(context, func, method); if (method.IsConstructor && method.DeclaringType.IsString()) { func.Body.Add(method.Apply(null, args).Return()); } else { var obj = "o".Id(); func.Body.Add(method.DeclaringType.New().Var(obj.Value)); func.Body.Add(method.Apply(obj, args).AsStatement()); func.Body.Add(obj.Return()); } return(data.Id()); }
private JsNode OpInitobj(MethodContext context, IType type) { var key = new InstructionKey(InstructionCode.Initobj, type); var info = context.Vars[key]; if (info != null) { return(info.Id()); } var func = new JsFunction(null); info = context.Vars.Add(key, func); CompileClass(type); InitClass(context, func, type); func.Body.Add(type.New().Return()); return(info.Id()); }
public JsProgram Compile() { if (_program != null) { return(_program); } _program = new JsProgram(); _program.Require("core.js"); var entryPoint = _assembly.EntryPoint; var method = CompileMethod(entryPoint); // system exceptions CompileClass(CorlibTypes[CorlibTypeId.NullReferenceException]); CompileClass(CorlibTypes[CorlibTypeId.InvalidCastException]); CompileClass(CorlibTypes[CorlibTypeId.NotImplementedException]); CompileClass(CorlibTypes[CorlibTypeId.IndexOutOfRangeException]); // build types CompileClass(SystemTypes.Type); new TypeInfoBuilder(this, _program).Build(); //TODO: pass args to main from node.js args var ctx = new MethodContext(this, CompileClass(entryPoint.DeclaringType), entryPoint, new TryCatchBlock[0]); var main = new JsFunction(null); InitClass(ctx, main, entryPoint); main.Body.Add(method.FullName.Id().Call().AsStatement()); _program.Add(main.Call().AsStatement()); return(_program); }
private static void CompileValues(JsCompiler compiler, JsClass klass) { var type = klass.Type; var fields = type.GetEnumFields() .Select(x => { var value = GetValue(compiler, x, type.ValueType); return(new { Name = x.Name, Value = value }); }); var typeName = type.JsFullName(); var valuesField = string.Format("{0}.$$values", typeName); object values; if (type.ValueType.IsInt64()) { values = new JsArray(fields.Select(x => (object)new JsObject { { "name", x.Name }, { "value", x.Value }, })); } else { values = new JsObject(fields.Select(x => new KeyValuePair <object, object>(x.Value, x.Name))); } klass.Add(new JsGeneratedField(valuesField, values)); var func = new JsFunction(null); func.Body.Add(valuesField.Id().Return()); klass.ExtendPrototype(func, "$values"); }
private string CompileBox(IType type, JsClass klass) { var name = string.Format("{0}.$box", type.JsFullName()); if (klass.BoxCompiled) { return(name); } klass.BoxCompiled = true; var val = (JsNode)"v".Id(); var func = new JsFunction(null, "v"); if (type.IsNullableInstance()) { func.Body.Add(new JsText(string.Format("if (!v.has_value) return null;"))); func.Body.Add("$copy".Id().Call(val).Return()); type = type.GetTypeArgument(0); CompileClass(type); } else { if (type.Is(SystemTypeCode.Boolean)) { val = val.Op("!!"); } func.Body.Add(type.New(val).Return()); } klass.Add(new JsGeneratedMethod(name, func)); return(name); }
private string CompileUnbox(MethodContext context, IType type, JsClass klass) { var name = string.Format("{0}.$unbox", type.JsFullName()); if (klass.UnboxCompiled) { return(name); } klass.UnboxCompiled = true; var valueType = type.GetTypeArgument(0); var unbox = Unbox(context, valueType); var val = (JsNode)"v".Id(); var func = new JsFunction(null, "v"); func.Body.Add(val.Set(unbox.Call(val))); func.Body.Add(type.New(val).Return()); klass.Add(new JsGeneratedMethod(name, func)); return(name); }
private void GetEnumeratorImpl(IMethod method) { var type = method.DeclaringType; if (type.IsGenericInstance()) { var elemType = type.GetTypeArgument(0); var enumerator = CompileArrayEnumerator(elemType); var ctor = enumerator.FindConstructor(1); var func = new JsFunction(); var obj = "o".Id(); func.Body.Add(enumerator.New().Var(obj.Value)); func.Body.Add(obj.Get(ctor.JsName()).Call("this".Id())); func.Body.Add(obj.Return()); AddImpl(method, func); } else { throw new NotImplementedException(); } }
private static void CompileGetHashCode(JsCompiler compiler, JsClass klass) { var type = klass.Type; var func = new JsFunction(null); var value = "this.$value".Id(); if (type.ValueType.IsInt64()) { //TODO: inline [U]Int64.GetHashCode implementation func.Body.Add(value.Get("GetHashCode").Call().Return()); } else if (!type.ValueType.Is(SystemTypeCode.Int32)) { var int32Type = compiler.SystemTypes.Int32; func.Body.Add("$conv".Id().Call(value, type.ValueType.JsTypeCode(), int32Type.JsTypeCode()).Return()); } else { func.Body.Add(value.Return()); } klass.ExtendPrototype(func, "GetHashCode"); }
internal void ExtendPrototype(IType type, JsFunction func, string name) { var klass = CompileClass(type); klass.ExtendPrototype(func, name); }
public void BoxUnboxed(MethodContext context, IType type, JsFunction func, JsNode obj) { var f = Box(context, type); func.Body.Add(obj.Set("$cbox".Id().Call(obj, f))); }
public JsGeneratedMethod(string fullName, JsFunction function) { _fullName = fullName; Function = function; }