private object OpLdtoken(MethodContext context, ITypeMember member) { var key = new InstructionKey(InstructionCode.Ldtoken, member); var var = context.Vars[key]; if (var != null) { return(var); } var field = member as IField; if (field != null) { if (field.IsArrayInitializer()) { var blob = field.GetBlob(); var arr = new JsArray(blob.ToArray().Select(x => (object)x)); return(context.Vars.Add(key, arr)); } throw new NotImplementedException(); } var type = member as IType; if (type != null) { CompileType(type); return(type.FullName); } throw new NotImplementedException(); }
private static bool IsSuperCall(MethodContext context, IMethod method, CallFlags flags) { bool thiscall = (flags & CallFlags.Thiscall) != 0; bool virtcall = (flags & CallFlags.Virtcall) != 0; return(thiscall && !virtcall && IsSuperCall(context, method)); }
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 object OpCall(MethodContext context, Instruction i) { var method = i.Method; var callInfo = context.Vars[method]; if (callInfo != null) { return(callInfo.Id()); } CompileCallMethod(method); var func = CreateCallFunc(context, method, i.CallInfo); callInfo = context.Vars[method]; if (callInfo != null) { return(callInfo.Id()); } var info = new JsObject { { "n", method.Parameters.Count }, { "s", method.IsStatic }, { "r", !method.IsVoid() }, { "f", func }, }; callInfo = context.Vars.Add(method, info); return(callInfo.Id()); }
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()); }
private static bool IsSuperCall(MethodContext context, IMethod method) { if (method.IsStatic || method.IsConstructor || method.IsAbstract) { return(false); } return(context.Method.IsBaseMethod(method)); }
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()); }
public JsNode Box(MethodContext context, IType type) { var klass = CompileClass(type); if (type.IsBoxableType() || type.IsNullableInstance()) { var name = CompileBox(type, klass); return(name.Id()); } return("$copy".Id()); }
private static object OpLeave(MethodContext context, Instruction i) { var index = -1; if (i.IsEndOfSehBlock) { var b = GetSurroundingTryFinally(i); if (b != null) { index = context.ProtectedBlocks.IndexOf(x => x == b); } } return(new JsTuple(i.Value, index)); }
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); }
public JsNode Unbox(MethodContext context, IType type) { var klass = CompileClass(type); //TODO: InvalidCastException if (type.IsNullableInstance()) { return(CompileUnbox(context, type, klass).Id()); } if (type.IsBoxableType()) { return("$unbox".Id()); } return("$copy".Id()); }
private object OpLdftn(MethodContext context, Instruction i) { var method = i.Method; var key = new InstructionKey(i.Code, method); var info = context.Vars[key]; if (info != null) { return(info); } CompileCallMethod(method); var func = CreateCallFunc(context, method, i.CallInfo); return(context.Vars.Add(key, func)); }
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 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); }
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 static IType ResolveSystemType(this MethodContext context, SystemTypeCode typeCode) { return(context.Method.DeclaringType.SystemType(typeCode)); }
private object CompileInstruction(MethodContext context, Instruction i) { //TODO: for every instruction set index of protected block to quickly find exception handlers var value = i.Value; if (value is long) { return(CompileInt64((long)value)); } if (value is ulong) { return(CompileUInt64((ulong)value)); } switch (i.Code) { // conv ops case InstructionCode.Conv_I1: case InstructionCode.Conv_I2: case InstructionCode.Conv_I4: case InstructionCode.Conv_I8: case InstructionCode.Conv_U1: case InstructionCode.Conv_U2: case InstructionCode.Conv_U4: case InstructionCode.Conv_U8: case InstructionCode.Conv_R4: case InstructionCode.Conv_R8: case InstructionCode.Conv_R_Un: case InstructionCode.Conv_Ovf_I1: case InstructionCode.Conv_Ovf_I2: case InstructionCode.Conv_Ovf_I4: case InstructionCode.Conv_Ovf_I8: case InstructionCode.Conv_Ovf_U1: case InstructionCode.Conv_Ovf_U2: case InstructionCode.Conv_Ovf_U4: case InstructionCode.Conv_Ovf_U8: // ldelem ops case InstructionCode.Ldelem_I1: case InstructionCode.Ldelem_I2: case InstructionCode.Ldelem_I4: case InstructionCode.Ldelem_I8: case InstructionCode.Ldelem_R4: case InstructionCode.Ldelem_R8: case InstructionCode.Ldelem_U1: case InstructionCode.Ldelem_U2: case InstructionCode.Ldelem_U4: // stelem ops case InstructionCode.Stelem_I1: case InstructionCode.Stelem_I2: case InstructionCode.Stelem_I4: case InstructionCode.Stelem_I8: case InstructionCode.Stelem_R4: case InstructionCode.Stelem_R8: // ldind ops case InstructionCode.Ldind_I1: case InstructionCode.Ldind_I2: case InstructionCode.Ldind_I4: case InstructionCode.Ldind_I8: case InstructionCode.Ldind_R4: case InstructionCode.Ldind_R8: case InstructionCode.Ldind_U1: case InstructionCode.Ldind_U2: case InstructionCode.Ldind_U4: return(OpWithTypeCode(i)); // stind ops case InstructionCode.Stind_I1: case InstructionCode.Stind_I2: case InstructionCode.Stind_I4: case InstructionCode.Stind_I8: case InstructionCode.Stind_R4: case InstructionCode.Stind_R8: return(OpStind(i)); case InstructionCode.Ldstr: // string should be compiled to be ready for Object method calls. CompileClass(SystemTypes.String); return(value); case InstructionCode.Call: case InstructionCode.Callvirt: return(OpCall(context, i)); case InstructionCode.Ldftn: case InstructionCode.Ldvirtftn: return(OpLdftn(context, i)); case InstructionCode.Calli: throw new NotImplementedException(); case InstructionCode.Newobj: return(OpNewobj(context, i.Method)); case InstructionCode.Initobj: return(OpInitobj(context, i.Type)); case InstructionCode.Newarr: return(OpNewarr(context, i.Type)); case InstructionCode.Ldfld: case InstructionCode.Ldsfld: case InstructionCode.Ldflda: case InstructionCode.Ldsflda: case InstructionCode.Stfld: case InstructionCode.Stsfld: CompileFields(i.Field.DeclaringType, i.Field.IsStatic); return(new FieldCompiler(this).Compile(context, i.Field)); case InstructionCode.Box: return(new BoxingImpl(this).Box(context, i.Type)); case InstructionCode.Unbox: case InstructionCode.Unbox_Any: return(new BoxingImpl(this).Unbox(context, i.Type)); case InstructionCode.Ldtoken: return(OpLdtoken(context, i.Member)); case InstructionCode.Isinst: case InstructionCode.Castclass: case InstructionCode.Ldobj: case InstructionCode.Stobj: CompileType(i.Type); return(i.Type.FullName); case InstructionCode.Ldelem: case InstructionCode.Ldelema: CompileType(i.Type); //TODO: add type name return(null); #region binary, unary arithmetic operations //arithmetic operations // a + b case InstructionCode.Add: return(Op(i, BinaryOperator.Addition, false, false)); case InstructionCode.Add_Ovf: return(Op(i, BinaryOperator.Addition, false, true)); case InstructionCode.Add_Ovf_Un: return(Op(i, BinaryOperator.Addition, true, true)); // a - b case InstructionCode.Sub: return(Op(i, BinaryOperator.Subtraction, false, false)); case InstructionCode.Sub_Ovf: return(Op(i, BinaryOperator.Subtraction, false, true)); case InstructionCode.Sub_Ovf_Un: return(Op(i, BinaryOperator.Subtraction, true, true)); // a * b case InstructionCode.Mul: return(Op(i, BinaryOperator.Multiply, false, false)); case InstructionCode.Mul_Ovf: return(Op(i, BinaryOperator.Multiply, false, true)); case InstructionCode.Mul_Ovf_Un: return(Op(i, BinaryOperator.Multiply, true, true)); // a / b case InstructionCode.Div: return(Op(i, BinaryOperator.Division, false, false)); case InstructionCode.Div_Un: return(Op(i, BinaryOperator.Division, true, false)); // a % b case InstructionCode.Rem: return(Op(i, BinaryOperator.Modulus, false, false)); case InstructionCode.Rem_Un: return(Op(i, BinaryOperator.Modulus, true, false)); //bitwise operations // a & b case InstructionCode.And: return(Op(i, BinaryOperator.BitwiseAnd, false, false)); // a | b case InstructionCode.Or: return(Op(i, BinaryOperator.BitwiseOr, false, false)); // a ^ b case InstructionCode.Xor: return(Op(i, BinaryOperator.ExclusiveOr, false, false)); // a << b case InstructionCode.Shl: return(Op(i, BinaryOperator.LeftShift, false, false)); // a >> b case InstructionCode.Shr: return(Op(i, BinaryOperator.RightShift, false, false)); case InstructionCode.Shr_Un: return(Op(i, BinaryOperator.RightShift, true, false)); //unary operations case InstructionCode.Neg: return(Op(i, UnaryOperator.Negate, false)); case InstructionCode.Not: return(Op(i, UnaryOperator.BitwiseNot, false)); //relation operations // a == b case InstructionCode.Ceq: return(Op(i, BinaryOperator.Equality, false, false)); // a > b case InstructionCode.Cgt: return(Op(i, BinaryOperator.GreaterThan, false, false)); case InstructionCode.Cgt_Un: return(Op(i, BinaryOperator.GreaterThan, true, false)); // a < b case InstructionCode.Clt: return(Op(i, BinaryOperator.LessThan, false, false)); case InstructionCode.Clt_Un: return(Op(i, BinaryOperator.LessThan, true, false)); #endregion #region conditional branches case InstructionCode.Beq: case InstructionCode.Beq_S: case InstructionCode.Bne_Un: case InstructionCode.Bne_Un_S: return(Br(i, BinaryOperator.Equality)); case InstructionCode.Blt: case InstructionCode.Blt_S: case InstructionCode.Blt_Un: case InstructionCode.Blt_Un_S: return(Br(i, BinaryOperator.LessThan)); case InstructionCode.Ble: case InstructionCode.Ble_S: case InstructionCode.Ble_Un: case InstructionCode.Ble_Un_S: return(Br(i, BinaryOperator.LessThanOrEqual)); case InstructionCode.Bgt: case InstructionCode.Bgt_S: case InstructionCode.Bgt_Un: case InstructionCode.Bgt_Un_S: return(Br(i, BinaryOperator.GreaterThan)); case InstructionCode.Bge: case InstructionCode.Bge_S: case InstructionCode.Bge_Un: case InstructionCode.Bge_Un_S: return(Br(i, BinaryOperator.GreaterThanOrEqual)); #endregion case InstructionCode.Leave: case InstructionCode.Leave_S: return(OpLeave(context, i)); } return(value); }