// Complete a first-kinded type structure. If type definition is higher kinded, this will // complete an instance of the type at the type arguments. Otherwise, this will complete // the type definition itself. private void BuildTypeExpression(Seq<JST.Statement> body, JST.Expression lhs) { TypeCompEnv.BindUsage(body, CollectPhase1Usage(), TypePhase.Id); // TODO: Replace with prototype body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootSetupTypeDefaults, TypeId.ToE())); EmitBaseAndSupertypes(body, lhs); EmitDefaultConstructor(body, lhs); EmitMemberwiseClone(body, lhs); EmitClone(body, lhs); EmitDefaultValue(body, lhs); EmitStaticMethods(body, lhs); EmitConstructObjectAndInstanceMethods(body, lhs); EmitVirtualAndInterfaceMethodRedirectors(body, lhs); EmitSetupType(body, lhs); EmitUnbox(body, lhs); EmitBox(body, lhs); EmitUnboxAny(body, lhs); EmitConditionalDeref(body, lhs); EmitIsValue(body, lhs); EmitEquals(body, lhs); EmitHash(body, lhs); EmitInterop(body, lhs); }
public void Emit() { if (Trace.Flavor == TraceFlavor.Remainder) { foreach (var kv in Trace.AssemblyMap) { var compiler = new AssemblyCompiler(this, kv.Value); compiler.Emit(null); } } else { var rootEnv = Env.Global.Environment(); var body = new Seq<JST.Statement>(); body.Add(JST.Statement.Var(RootId, new JST.Identifier(Env.Root).ToE())); foreach (var nm in rootEnv.AllLoadedAssembliesInLoadOrder().Where(Trace.AssemblyMap.ContainsKey)) { var compiler = new AssemblyCompiler(this, Trace.AssemblyMap[nm]); compiler.Emit(body); } var program = new JST.Program (new JST.Statements (new JST.ExpressionStatement (new JST.StatementsPseudoExpression(new JST.Statements(body), null)))); var fileName = Path.Combine(Env.OutputDirectory, Trace.Name + ".js"); program.ToFile(fileName, Env.PrettyPrint); Env.Log(new GeneratedJavaScriptFile("trace '" + Trace.Name + "'", fileName)); } }
public CallContext(IImSeq<Identifier> parameters, IImSeq<Expression> arguments, Func<Expression, bool> isValue) { var paramMap = new Map<Identifier, int>(); for (var i = 0; i < parameters.Count; i++) paramMap.Add(parameters[i], i); Parameters = paramMap; var argumentEffects = new Seq<Effects>(parameters.Count); SeenParameters = new Seq<bool>(parameters.Count); var allReadOnly = true; AllArgumentEffects = Effects.Bottom; foreach (var e in arguments) { var fxCtxt = new EffectsContext(isValue); e.AccumEffects(fxCtxt, null, null); argumentEffects.Add(fxCtxt.AccumEffects); AllArgumentEffects = AllArgumentEffects.Lub(fxCtxt.AccumEffects); if (!fxCtxt.AccumEffects.IsReadOnly) allReadOnly = false; SeenParameters.Add(false); } ArgumentEffects = argumentEffects; AllReadOnly = allReadOnly; IsOk = true; }
public CallContext(CompilationEnvironment outerCompEnv, CompilationEnvironment inlinedCompEnv, IImSeq<Expression> arguments) { var paramMap = new Map<JST.Identifier, int>(); for (var i = 0; i < inlinedCompEnv.Method.Arity; i++) paramMap.Add(inlinedCompEnv.ValueParameterIds[i], i); Parameters = paramMap; var argumentEffects = new Seq<JST.Effects>(inlinedCompEnv.Method.Arity); SeenParameters = new Seq<bool?>(inlinedCompEnv.Method.Arity); AllArgumentEffects = JST.Effects.Bottom; var allReadOnly = true; foreach (var e in arguments) { var fxCtxt = new JST.EffectsContext(null); e.AccumEffects(fxCtxt, null, null); argumentEffects.Add(fxCtxt.AccumEffects); AllArgumentEffects = AllArgumentEffects.Lub(fxCtxt.AccumEffects); if (!fxCtxt.AccumEffects.IsReadOnly) allReadOnly = false; SeenParameters.Add(e.IsValue(outerCompEnv) ? default(bool?) : false); } ArgumentEffects = argumentEffects; AllReadOnly = allReadOnly; IsOk = true; }
// ---------------------------------------------------------------------- // Entry point from AssemblyCompiler // ---------------------------------------------------------------------- public void Emit(Seq<JST.Statement> body) { if (Env.BreakOnBreak && Env.AttributeHelper.TypeHasAttribute(TyconEnv.Assembly, TyconEnv.Type, Env.AttributeHelper.BreakAttributeRef, false, false)) System.Diagnostics.Debugger.Break(); CollectMembers(); var typeName = CST.CSTWriter.WithAppend (Env.Global, CST.WriterStyle.Uniform, TyconEnv.Type.EffectiveName(Env.Global).Append); var slotName = Env.GlobalMapping.ResolveTypeDefToSlot(TyconEnv.Assembly, TyconEnv.Type); if (TypeTrace != null && TypeTrace.IncludeType && TypeTrace.Parent.Parent.Flavor == TraceFlavor.Remainder) { // Self-loader fragment var assmName = CST.CSTWriter.WithAppend (Env.Global, CST.WriterStyle.Uniform, TyconEnv.Assembly.Name.Append); var funcBody = new Seq<JST.Statement>(); BuildTypeStructure(funcBody); var func = new JST.FunctionExpression (new Seq<JST.Identifier> { RootId, AssemblyId, TypeDefinitionId }, new JST.Statements(funcBody)); var loaderBody = new Seq<JST.Statement>(); if (Env.DebugMode) loaderBody.Add(new JST.CommentStatement(TyconEnv.ToString())); loaderBody.Add (JST.Statement.DotCall (new JST.Identifier(Env.Root).ToE(), Constants.RootBindType, new JST.StringLiteral(assmName), new JST.StringLiteral(slotName), new JST.StringLiteral(typeName), func)); var program = new JST.Program(new JST.Statements(loaderBody)); var typePath = Path.Combine (Path.Combine(Env.OutputDirectory, JST.Lexemes.StringToFileName(assmName)), slotName); var fileName = Path.Combine(typePath, Constants.TypeFileName); program.ToFile(fileName, Env.PrettyPrint); Env.Log(new GeneratedJavaScriptFile("type '" + TyconEnv.TypeConstructorRef + "'", fileName)); CompileMethods(null, NameSupply); } else if (TypeTrace != null && !TypeTrace.IncludeType && TypeTrace.Parent.Parent.Flavor == TraceFlavor.Remainder) { // Just passisng through CompileMethods(body, NameSupply); } else if (TypeTrace != null && !TypeTrace.IncludeType) { // Type defined elsewhere, include some/all methods only body.Add (JST.Statement.Var (TypeDefinitionId, JST.Expression.DotCall (AssemblyId.ToE(), new JST.Identifier(Constants.AssemblyTypeBuilderSlot(slotName)), Env.JSTHelpers.PhaseExpression(TypePhase.Slots)))); CompileMethods(body, NameSupply); } else { // Inline type definition and some/all methods if (Env.DebugMode) body.Add(new JST.CommentStatement(TyconEnv.ToString())); // We must construct the type explicity to phase 1 rather than using type compiler environment // since it thinks the type is already at phase 2. body.Add (JST.Statement.Var (TypeDefinitionId, JST.Expression.DotCall (AssemblyId.ToE(), new JST.Identifier(Constants.AssemblyTypeBuilderSlot(slotName)), Env.JSTHelpers.PhaseExpression(TypePhase.Id)))); BuildTypeStructure(body); CompileMethods(body, NameSupply); } }
// ---------------------------------------------------------------------- // Building type structures // ---------------------------------------------------------------------- private void BuildTypeStructure(Seq<JST.Statement> body) { // Already bound: T, Id, Assembly, Name, Slot var compiler = new TypeCompiler(this); compiler.Emit(body); if (TyconEnv.Type.Arity > 0) { // Methods live on type definition itself if type is higher-kinded if (Env.DebugMode) body.Add(new JST.CommentStatement("Method definitions (accepting type-bound type arguments)")); EmitMethods(body, TypeDefinitionId.ToE(), NameSupply, TypeDefinitionId.ToE(), false); EmitMethods(body, TypeDefinitionId.ToE(), NameSupply, TypeDefinitionId.ToE(), true); } // Shared strings if (Env.DebugMode) body.Add(new JST.CommentStatement("Shared strings")); foreach (var kv in Env.GlobalMapping.AllStringSlots(TyconEnv.Assembly, TyconEnv.Type)) { body.Add (JST.Statement.DotAssignment (TypeDefinitionId.ToE(), new JST.Identifier(Constants.TypeStringSlot(kv.Value)), new JST.StringLiteral(kv.Key))); } }
// Imports: // - instance methods: no qualification // - static methods & constructors: add qualification // Exports: // - instance methods, not prototype bound: no qualification // - instance methods, prototype bound: add qualification and 'prototype' // - static methods & constructors: add qualification private JST.Expression PrefixName(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, bool isExport) { if (script != null && script is JST.FunctionExpression) return script; var isNonInstance = methodDef.IsStatic || methodDef.IsConstructor; var qual = default(Qualification); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheQualificationProperty, true, false, ref qual); var bindToProto = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheBindToPrototypeProperty, true, false, ref bindToProto); var isProto = isExport && bindToProto; var path = new Seq<JST.PropertyName>(); if (script == null && !methodDef.IsStatic && methodDef.IsConstructor && qual == Qualification.None) qual = Qualification.Type; if (!isExport && !isNonInstance && qual != Qualification.None) qual = Qualification.None; if (isExport && !isNonInstance && !isProto && qual != Qualification.None) qual = Qualification.None; if (isExport && !isNonInstance && isProto && qual == Qualification.None) qual = Qualification.Type; if (isNonInstance) { var global = default(JST.Expression); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheGlobalObjectProperty, true, false, ref global); if (global != null) { if (global is JST.FunctionExpression) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); env.Log(new InvalidInteropMessage(ctxt, "global object expression cannot be a function")); throw new DefinitionException(); } foreach (var p in JST.Expression.ExplodePath(global)) path.Add(p); } } if (qual == Qualification.Full) { var nm = typeDef.EffectiveName(env.Global); if (nm.Namespace.Length > 0) { var nsCasing = default(Casing); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.NamingAttributeRef, attributeHelper.TheNamespaceCasingProperty, true, false, ref nsCasing); foreach (var n in nm.Namespace.Split('.')) path.Add(new JST.PropertyName(Recase(n, nsCasing))); } } if (qual == Qualification.Full || qual == Qualification.Type) { var tnCasing = default(Casing); attributeHelper.GetValueFromType (assemblyDef, typeDef, attributeHelper.NamingAttributeRef, attributeHelper.TheTypeNameCasingProperty, true, false, ref tnCasing); foreach (var n in typeDef.EffectiveName(env.Global).Types.Select (name => new JST.PropertyName(Recase(name, tnCasing)))) path.Add(n); } if (isProto) path.Add(new JST.PropertyName(Constants.prototype)); if (script != null) { foreach (var p in JST.Expression.ExplodePath(script)) path.Add(p); } return JST.Expression.Path(path); }
private void EnsurePathExists(ISeq<JST.Statement> statements, JST.Expression script, bool isStatic) { var path = JST.Expression.ExplodePath(script); for (var i = isStatic ? 0 : 1; i < path.Count - 1; i++) { var prefixPath = new Seq<JST.PropertyName>(); for (var j = 0; j <= i; j++) prefixPath.Add(path[j]); var prefix = JST.Expression.Path(prefixPath); if (i == 0) { var exId = new JST.Identifier("e"); #if !JSCRIPT_IS_CORRECT statements.Add(JST.Statement.Var(exId)); #endif statements.Add (new JST.TryStatement (new JST.Statements(new JST.ExpressionStatement(prefix)), new JST.CatchClause (exId, new JST.Statements(JST.Statement.Assignment(prefix, new JST.ObjectLiteral()))))); } else if (!path[i].Value.Equals(Constants.prototype.Value, StringComparison.Ordinal)) statements.Add (new JST.IfStatement (JST.Expression.IsNull(prefix), new JST.Statements(JST.Statement.Assignment(prefix, new JST.ObjectLiteral())))); } }
internal void UsedByMembersClosure(Global global, AssemblyDef assemblyDef, TypeDef typeDef, Set<QualifiedMemberName> visitedMemberDefs, Seq<QualifiedMemberName> scc) { if (usedByMembers == null) return; var self = QualifiedMemberName(global, assemblyDef, typeDef); if (!visitedMemberDefs.Contains(self)) { visitedMemberDefs.Add(self); scc.Add(self); foreach (var r in usedByMembers) { var usedByAssemblyDef = default(AssemblyDef); var usedByTypeDef = default(TypeDef); var usedByMemberDef = default(MemberDef); if (r.PrimTryResolve(global, out usedByAssemblyDef, out usedByTypeDef, out usedByMemberDef)) usedByMemberDef.UsedByMembersClosure (global, usedByAssemblyDef, usedByTypeDef, visitedMemberDefs, scc); } } }
private LoopClause LoopClause() { var loc = Only("loop clause", "'('", InputElementTag.LParen); if (Current.Tag == InputElementTag.Semicolon) { Consume(); var condition = default(Expression); if (Current.Tag != InputElementTag.Semicolon) condition = Expression(false); Only("loop clause", "';'", InputElementTag.Semicolon); if (condition != null) condition = ConsumeCommentExpression(condition); var increment = default(Expression); if (Current.Tag != InputElementTag.RParen) increment = Expression(false); loc = loc.Union(Only("loop clause", "')'", InputElementTag.RParen)); if (increment != null) increment = ConsumeCommentExpression(increment); return new ForLoopClause(loc, null, condition, increment); } else if (Current.Tag == InputElementTag.Var) { Consume(); var vd = VariableDeclaration(true); if (Current.Tag == InputElementTag.In) { Consume(); var collection = Expression(false); loc = loc.Union(Only("loop clause", "')'", InputElementTag.RParen)); collection = ConsumeCommentExpression(collection); return new ForEachVarLoopClause(loc, vd, collection); } else { var iterationVariables = new Seq<VariableDeclaration>(); iterationVariables.Add(vd); while (Current.Tag == InputElementTag.Comma) { Consume(); iterationVariables.Add(VariableDeclaration(true)); } Only("loop clause", "';'", InputElementTag.Semicolon); var condition = default(Expression); if (Current.Tag != InputElementTag.Semicolon) condition = Expression(false); Only("loop clause", "';'", InputElementTag.Semicolon); if (condition != null) condition = ConsumeCommentExpression(condition); var increment = default(Expression); if (Current.Tag != InputElementTag.RParen) increment = Expression(false); loc = loc.Union(Only("loop clause", "')'", InputElementTag.RParen)); if (increment != null) increment = ConsumeCommentExpression(increment); return new ForVarLoopClause(loc, iterationVariables, condition, increment); } } else { var isLHS = true; var i = Expression(true, ref isLHS); if (Current.Tag == InputElementTag.Semicolon) { var initializer = i; Consume(); initializer = ConsumeCommentExpression(initializer); var condition = default(Expression); if (Current.Tag != InputElementTag.Semicolon) condition = Expression(false); Only("loop clause", "';'", InputElementTag.Semicolon); if (condition != null) condition = ConsumeCommentExpression(condition); var increment = default(Expression); if (Current.Tag != InputElementTag.RParen) increment = Expression(false); loc = loc.Union(Only("loop clause", "')'", InputElementTag.RParen)); if (increment != null) increment = ConsumeCommentExpression(increment); return new ForLoopClause(loc, initializer, condition, increment); } else if (isLHS) { Only("loop clause", "'in'", InputElementTag.In); var iterationVariable = i; iterationVariable = ConsumeCommentExpression(iterationVariable); var collection = Expression(false); loc = loc.Union(Only("loop clause", "')'", InputElementTag.RParen)); collection = ConsumeCommentExpression(collection); return new ForEachLoopClause(loc, iterationVariable, collection); } else throw MsgError("loop clause", "syntax error"); } }
// Take acccount of // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private ImportMethodInfo FinalImportScript(MessageContext ctxt, Func<JST.Identifier> gensym, JST.Identifier rootId, CCI.Method methodDefn, JST.Expression script, bool isNew) { if (script == null) throw new InvalidOperationException("expecting default script value"); var lastArgIsParamsArray = LastArgIsParamsArray(ctxt, methodDefn) && interopTypes.GetValue(ctxt, methodDefn, env.ImportAttributeType, interopTypes.TheInlineParamsArrayProperty); var methodArity = Arity(methodDefn); var isInstanceMethod = !(methodDefn.IsStatic || methodDefn is CCI.InstanceInitializer); var scriptExpectsRoot = interopTypes.GetValue (ctxt, methodDefn, env.ImportAttributeType, interopTypes.ThePassRootAsArgumentProperty); var instanceIsThis = isInstanceMethod && !interopTypes.GetValue (ctxt, methodDefn, env.ImportAttributeType, interopTypes.ThePassInstanceAsArgumentProperty); var expectedScriptArity = methodArity - (lastArgIsParamsArray ? 1 : 0) + (scriptExpectsRoot ? 1 : 0) - (instanceIsThis ? 1 : 0); CheckScriptArity(ctxt, methodDefn, script, expectedScriptArity); var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); var callArgs = new Seq<JST.Expression>(); if (lastArgIsParamsArray) { var argsId = gensym(); body.Add(JST.Statement.Var(argsId, new JST.ArrayLiteral())); if (scriptExpectsRoot) body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, rootId.ToE())); if (!isInstanceMethod) callArgs.Add(new JST.NullExpression()); for (var i = 0; i < methodArity; i++) { var id = gensym(); parameters.Add(id); if (isInstanceMethod && i == 0) { if (instanceIsThis) callArgs.Add(id.ToE()); else { callArgs.Add(new JST.NullExpression()); body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, id.ToE())); } } else if (i == methodArity - 1) { var iId = gensym(); body.Add (new JST.IfStatement (JST.Expression.IsNotNull(id.ToE()), new JST.Statements (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(0), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(id.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (argsId.ToE(), Constants.push, new JST.IndexExpression(id.ToE(), iId.ToE()))))))); } else body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, id.ToE())); } if (script is JST.FunctionExpression) { var funcId = gensym(); body.Add(JST.Statement.Var(funcId, script)); script = JST.Expression.Dot(funcId.ToE(), Constants.apply); } else script = JST.Expression.Dot(script, Constants.apply); callArgs.Add(argsId.ToE()); } else { if (scriptExpectsRoot) callArgs.Add(rootId.ToE()); for (var i = 0; i < methodArity; i++) { var id = gensym(); parameters.Add(id); if (i == 0 && instanceIsThis) { if (script is JST.FunctionExpression) { callArgs.Insert(0, id.ToE()); var funcId = gensym(); body.Add(JST.Statement.Var(funcId, script)); script = JST.Expression.Dot(funcId.ToE(), Constants.call); } else script = JST.Expression.Dot(id.ToE(), JST.Expression.ExplodePath(script)); } else callArgs.Add(id.ToE()); } } var exp = (JST.Expression)new JST.CallExpression(script, callArgs); if (isNew) exp = new JST.NewExpression(exp); if (ReturnType(methodDefn) == null) body.Add(new JST.ExpressionStatement(exp)); else body.Add(new JST.ReturnStatement(exp)); function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ImportMethodInfo { MethodDefn = methodDefn, Script = function }; }
// Imports: // - instance methods: no qualification // - static methods & constructors: add qualification // Exports: // - instance methods, not prototype bound: no qualification // - instance methods, prototype bound: add qualification and 'prototype' // - static methods & constructors: add qualification private JST.Expression PrefixName(MessageContext ctxt, CCI.Member member, JST.Expression script, bool isExport) { if (script != null && script is JST.FunctionExpression) return script; var isNonInstance = member.IsStatic || member is CCI.InstanceInitializer; var qual = interopTypes.GetValue (ctxt, member, env.NamingAttributeType, interopTypes.TheQualificationProperty); var isProto = isExport && interopTypes.GetValue (ctxt, member, env.ExportAttributeType, interopTypes.TheBindToPrototypeProperty); var path = new Seq<JST.PropertyName>(); if (script == null && member is CCI.InstanceInitializer && qual == Qualification.None) qual = Qualification.Type; if (!isExport && !isNonInstance && qual != Qualification.None) qual = Qualification.None; if (isExport && !isNonInstance && !isProto && qual != Qualification.None) qual = Qualification.None; if (isExport && !isNonInstance && isProto && qual == Qualification.None) qual = Qualification.Type; if (isNonInstance) { var global = interopTypes.GetValue (ctxt, member, env.NamingAttributeType, interopTypes.TheGlobalObjectProperty); if (global != null) { if (global is JST.FunctionExpression) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Member(ctxt, member), "global object expression cannot be a function")); throw new DefinitionException(); } foreach (var p in JST.Expression.ExplodePath(global)) path.Add(p); } } if (qual == Qualification.Full) { var nsCasing = interopTypes.GetValue (ctxt, member, env.NamingAttributeType, interopTypes.TheNamespaceCasingProperty); foreach (var name in member.DeclaringType.Namespace.Name.Split('.')) { if (string.IsNullOrEmpty(name)) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Member(ctxt, member), "member's namespace cannot be represented in JavaScript")); throw new DefinitionException(); } path.Add(new JST.PropertyName(Recase(name, nsCasing))); } } if (qual == Qualification.Full || qual == Qualification.Type) { var type = member.DeclaringType; var i = path.Count; do { var tnCasing = interopTypes.GetValue (ctxt, type, env.NamingAttributeType, interopTypes.TheTypeNameCasingProperty); path.Insert(i, new JST.PropertyName(Recase(type.Name.Name, tnCasing))); type = type.DeclaringType; } while (type != null); } if (isProto) path.Add(new JST.PropertyName(Constants.prototype)); if (script != null) { foreach (var p in JST.Expression.ExplodePath(script)) path.Add(p); } return JST.Expression.Path(path); }
public void Emit() { var assm = typeof (RuntimeCompiler).Assembly; var res = "Microsoft.LiveLabs.JavaScript.IL2JS." + Constants.RuntimeFileName; var runtime = default(JST.Program); using (var runtimeStream = assm.GetManifestResourceStream(res)) { if (runtimeStream == null) throw new InvalidOperationException("unable to find runtime resource"); runtime = JST.Program.FromStream(Constants.RuntimeFileName, runtimeStream, true); } var mode = default(string); switch (env.CompilationMode) { case CompilationMode.Plain: mode = "plain"; break; case CompilationMode.Collecting: mode = "collecting"; break; case CompilationMode.Traced: mode = "traced"; break; default: throw new ArgumentOutOfRangeException(); } var body = default(ISeq<JST.Statement>); if (env.DebugMode) { body = new Seq<JST.Statement>(); body.Add (JST.Statement.Var(Constants.DebugLevel, new JST.NumericLiteral(env.DebugLevel))); body.Add(JST.Statement.Var(Constants.DebugId, new JST.BooleanLiteral(true))); body.Add(JST.Statement.Var(Constants.ModeId, new JST.StringLiteral(mode))); body.Add(JST.Statement.Var(Constants.SafeId, new JST.BooleanLiteral(env.SafeInterop))); foreach (var s in runtime.Body.Body) body.Add(s); } else { // Simplify var simpCtxt = new JST.SimplifierContext(true, env.DebugMode, new JST.NameSupply(Constants.Globals), null). InFreshStatements(); simpCtxt.Bind(Constants.DebugId, new JST.BooleanLiteral(false)); simpCtxt.Bind(Constants.ModeId, new JST.StringLiteral(mode)); simpCtxt.Bind(Constants.SafeId, new JST.BooleanLiteral(env.SafeInterop)); simpCtxt.Add(JST.Statement.Var(Constants.DebugLevel, new JST.NumericLiteral(env.DebugLevel))); runtime.Body.Simplify(simpCtxt, EvalTimes.Bottom, false); body = simpCtxt.Statements; } var opts = new OrdMap<JST.Identifier, JST.Expression>(); var mscorlibName = new JST.StringLiteral (CST.CSTWriter.WithAppend(env.Global, CST.WriterStyle.Uniform, env.Global.MsCorLibName.Append)); opts.Add(Constants.SetupMscorlib, mscorlibName); var target = default(string); switch (env.Target) { case Target.Browser: target = "browser"; break; case Target.CScript: target = "cscript"; break; default: throw new ArgumentOutOfRangeException(); } opts.Add(Constants.SetupTarget, new JST.StringLiteral(target)); var loadPaths = env.LoadPaths.Select<string, JST.Expression>(FixupPath).ToSeq(); if (loadPaths.Count == 0) loadPaths.Add(new JST.StringLiteral("")); opts.Add(Constants.SetupSearchPaths, new JST.ArrayLiteral(loadPaths)); if (env.DebugMode) body.Add(new JST.CommentStatement("Setup runtime")); var rootId = new JST.Identifier(env.Root); body.Add(JST.Statement.Var(rootId, new JST.ObjectLiteral())); body.Add(JST.Statement.Call(Constants.NewRuntime.ToE(), rootId.ToE(), new JST.ObjectLiteral(opts))); var program = new JST.Program(new JST.Statements(body)); var runtimeFileName = Path.Combine(env.OutputDirectory, Constants.RuntimeFileName); program.ToFile(runtimeFileName, env.PrettyPrint); env.Log(new GeneratedJavaScriptFile("runtime", runtimeFileName)); }
public static TypeCompilerEnvironment EnterType (CompilerEnvironment env, JST.NameSupply nameSupply, JST.Identifier rootId, JST.Identifier assemblyId, JST.Identifier typeId, CST.TypeEnvironment typeEnv, TypeTrace typeTrace) { var typeBoundTypeParameterIds = new Seq<JST.Identifier>(); for (var i = 0; i < typeEnv.Type.Arity; i++) typeBoundTypeParameterIds.Add(nameSupply.GenSym()); var res = new TypeCompilerEnvironment (typeEnv.Global, typeEnv.SkolemDefs, typeEnv.Assembly, typeEnv.Type, typeEnv.TypeBoundArguments, env, nameSupply, rootId, assemblyId, typeId, typeBoundTypeParameterIds, typeTrace); res.BindSpecial(); return res; }
public static ISeq<BBLoop> Loops(BasicBlock root) { var res = new Seq<BBLoop>(); var backEdges = BackEdges(root); foreach (var backEdge in backEdges) { var body = new Set<BasicBlock> { backEdge.Target }; ReachableFrom(backEdge.Source, body); res.Add(new BBLoop(backEdge.Target, backEdge.Source, body, null)); } return res; }
private void PushOpenPunctuator(InputElement openElem) { openPunctuators.Add(openElem); }
private Location VariableDeclarations(Seq<VariableDeclaration> variables) { // Current is 'var' var loc = Current.Loc; Consume(); while (true) { var vd = VariableDeclaration(false); variables.Add(vd); loc = loc.Union(vd.Loc); if (Current.Tag == InputElementTag.Comma) Consume(); else break; } OptSemicolon("variable declarations"); return loc; }
public ImportMethodInfo ImportInfo(MessageContext ctxt, Func<JST.Identifier> gensym, JST.Identifier rootId, CCI.Method methodDefn) { if (!IsImported(ctxt, methodDefn)) return null; if (gensym != null) CheckParameterAndReturnTypesAreImportableExportable(ctxt, methodDefn); var methodArity = Arity(methodDefn); var script = interopTypes.GetValue(ctxt, methodDefn, env.ImportAttributeType, interopTypes.TheScriptProperty); if (methodDefn is CCI.InstanceInitializer) { // XREF1171 // Constructor if (script == null) { switch ( interopTypes.GetValue (ctxt, methodDefn, env.ImportAttributeType, interopTypes.TheCreationProperty)) { case Creation.Constructor: script = PrefixName(ctxt, methodDefn, null, false); break; case Creation.Object: if (methodArity > 0) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "imported constructors for object literals cannot have arguments")); throw new DefinitionException(); } script = Constants.Object.ToE(); break; case Creation.Array: script = Constants.Array.ToE(); break; default: throw new ArgumentOutOfRangeException(); } return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, true); } else if (script is JST.FunctionExpression) return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, false); else { script = PrefixName(ctxt, methodDefn, script, false); return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, true); } } else { if (methodDefn.DeclaringMember != null) { var isOnMethod = interopTypes.HasAttribute(methodDefn, env.ImportAttributeType, false); var localScript = isOnMethod ? interopTypes.GetValue(ctxt, methodDefn, env.ImportAttributeType, interopTypes.TheScriptProperty, false) : default(JST.Expression); var prop = methodDefn.DeclaringMember as CCI.Property; if (prop != null) { // XREF1187 if (methodDefn == prop.Getter) { // Getter if (isOnMethod) { script = PrefixName (ctxt, methodDefn, GetterSetterAdderRemoverNameFromMethod(ctxt, methodDefn, "get", localScript), false); return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, false); } else if (script != null && script is JST.FunctionExpression) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "property import script cannot be a function")); throw new DefinitionException(); } else { var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); for (var i = 0; i < methodArity; i++) parameters.Add(gensym()); if (script == null && methodArity == 2 && !methodDefn.IsStatic) body.Add (new JST.ReturnStatement (new JST.IndexExpression (parameters[0].ToE(), parameters[1].ToE()))); else { script = PrefixName (ctxt, methodDefn, RecasePropertyEvent(ctxt, methodDefn, script), false); if (methodDefn.IsStatic && methodArity == 0) body.Add(new JST.ReturnStatement(script)); else if (!methodDefn.IsStatic && methodArity == 1) body.Add (new JST.ReturnStatement (JST.Expression.Dot (parameters[0].ToE(), JST.Expression.ExplodePath(script)))); else { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "additional getter parameters not supported for default getters")); throw new DefinitionException(); } } function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ImportMethodInfo { MethodDefn = methodDefn, Script = function }; } } else if (methodDefn == prop.Setter) { // Setter if (isOnMethod) { script = PrefixName (ctxt, methodDefn, GetterSetterAdderRemoverNameFromMethod(ctxt, methodDefn, "set", localScript), false); return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, false); } else if (script != null && script is JST.FunctionExpression) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "property import script cannot be a function")); throw new DefinitionException(); } else { var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); for (var i = 0; i < methodArity; i++) parameters.Add(gensym()); if (script == null && methodArity == 3 && !methodDefn.IsStatic) body.Add (JST.Statement.IndexAssignment (parameters[0].ToE(), parameters[1].ToE(), parameters[2].ToE())); else { script = PrefixName (ctxt, methodDefn, RecasePropertyEvent(ctxt, methodDefn, script), false); if (methodDefn.IsStatic && methodArity == 1) body.Add (JST.Statement.Assignment(script, parameters[0].ToE())); else if (!methodDefn.IsStatic && methodArity == 2) body.Add (JST.Statement.Assignment (JST.Expression.Dot (parameters[0].ToE(), JST.Expression.ExplodePath(script)), parameters[1].ToE())); else { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "additional setter parameters not supported for default setters")); throw new DefinitionException(); } } function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ImportMethodInfo { MethodDefn = methodDefn, Script = function }; } } else throw new InvalidOperationException(); } else { var evnt = methodDefn.DeclaringMember as CCI.Event; if (evnt != null) { // XREF1201 if (methodDefn == evnt.HandlerAdder) { // Adder if (isOnMethod) { script = PrefixName (ctxt, methodDefn, GetterSetterAdderRemoverNameFromMethod(ctxt, methodDefn, "add", localScript), false); return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, false); } else if (script != null && script is JST.FunctionExpression) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "event import script cannot be a function")); throw new DefinitionException(); } else { var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); for (var i = 0; i < methodArity; i++) parameters.Add(gensym()); script = PrefixName (ctxt, methodDefn, RecasePropertyEvent(ctxt, methodDefn, script), false); if (methodDefn.IsStatic) body.Add (JST.Statement.Assignment(script, parameters[0].ToE())); else body.Add (JST.Statement.Assignment (JST.Expression.Dot (parameters[0].ToE(), JST.Expression.ExplodePath(script)), parameters[1].ToE())); function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ImportMethodInfo { MethodDefn = methodDefn, Script = function }; } } else if (methodDefn == evnt.HandlerRemover) { // Remover if (isOnMethod) { script = PrefixName (ctxt, methodDefn, GetterSetterAdderRemoverNameFromMethod(ctxt, methodDefn, "remove", localScript), false); return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, false); } else if (script != null && script is JST.FunctionExpression) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "event import script cannot be a function")); throw new DefinitionException(); } else { var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); for (var i = 0; i < methodArity; i++) parameters.Add(gensym()); script = PrefixName (ctxt, methodDefn, RecasePropertyEvent(ctxt, methodDefn, script), false); if (methodDefn.IsStatic) body.Add (JST.Statement.Assignment(script, parameters[0].ToE())); else body.Add (JST.Statement.Assignment (JST.Expression.Dot (parameters[0].ToE(), JST.Expression.ExplodePath(script)), parameters[1].ToE())); function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ImportMethodInfo { MethodDefn = methodDefn, Script = function }; } } else throw new InvalidOperationException(); } else throw new InvalidOperationException(); } } else { // XREF1153 // Normal method script = PrefixName(ctxt, methodDefn, RecaseMember(ctxt, methodDefn, script), false); return FinalImportScript(ctxt, gensym, rootId, methodDefn, script, false); } } }
internal void TopologicalAllDeps(Global global, AssemblyDef assemblyDef, TypeDef typeDef, Set<QualifiedMemberName> visitedMemberDefs, Seq<QualifiedMemberName> sortedMemberDefs) { var self = QualifiedMemberName(global, assemblyDef, typeDef); if (visitedMemberDefs.Contains(self)) return; visitedMemberDefs.Add(self); if (usedTypes == null) return; foreach (var r in usedTypes) { var usedAssemblyDef = default(AssemblyDef); var usedTypeDef = default(TypeDef); if (r.PrimTryResolve(global, out usedAssemblyDef, out usedTypeDef)) usedTypeDef.UsedBy(self); } foreach (var r in usedMembers) { var usedAssemblyDef = default(AssemblyDef); var usedTypeDef = default(TypeDef); var usedMemberDef = default(MemberDef); if (r.PrimTryResolve(global, out usedAssemblyDef, out usedTypeDef, out usedMemberDef)) { usedMemberDef.UsedBy(self); usedMemberDef.TopologicalAllDeps (global, usedAssemblyDef, usedTypeDef, visitedMemberDefs, sortedMemberDefs); } } sortedMemberDefs.Add(self); }
// Take account of: // - BindToPrototype // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private ExportMethodInfo FinalExportInfo(MessageContext ctxt, Func<JST.Identifier> gensym, JST.Identifier rootId, CCI.Method methodDefn, JST.Expression script) { if (script == null) throw new InvalidOperationException("expecting default script value"); var isInstance = !methodDefn.IsStatic && !(methodDefn is CCI.InstanceInitializer); if (isInstance) { var declType = methodDefn.DeclaringType; if (declType.IsValueType) { env.Log(new InvalidInteropMessage (RewriterMsgContext.Method(ctxt, methodDefn), "cannot export instance methods from value types")); throw new DefinitionException(); } } var lastArgIsParamsArray = LastArgIsParamsArray(ctxt, methodDefn) && interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.TheInlineParamsArrayProperty); var isPassRoot = interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.ThePassRootAsArgumentProperty); var isProto = interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.TheBindToPrototypeProperty); var isPassInstance = interopTypes.GetValue (ctxt, methodDefn, env.ExportAttributeType, interopTypes.ThePassInstanceAsArgumentProperty); var bindToInstance = isInstance && !isProto; var captureThis = isInstance && !isPassInstance; var expectedScriptArity = (isPassRoot ? 1 : 0) + (bindToInstance ? 1 : 0) + 1; CheckScriptArity(ctxt, methodDefn, script, expectedScriptArity); var function = default(JST.FunctionExpression); if (gensym != null) { var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); var callArgs = new Seq<JST.Expression>(); if (isPassRoot) callArgs.Add(rootId.ToE()); var instArgId = default(JST.Identifier); if (bindToInstance) { instArgId = gensym(); parameters.Add(instArgId); callArgs.Add(instArgId.ToE()); } var funcArgId = gensym(); parameters.Add(funcArgId); if (captureThis || lastArgIsParamsArray) { var innerParameters = new Seq<JST.Identifier>(); var innerBody = new Seq<JST.Statement>(); var innerArgs = new Seq<JST.Expression>(); var methodArity = Arity(methodDefn); for (var i = 0; i < methodArity; i++) { if (i == 0 && captureThis) innerArgs.Add(new JST.ThisExpression()); else if (i == methodArity - 1 && lastArgIsParamsArray) { var iId = gensym(); var arrId = gensym(); innerBody.Add(JST.Statement.Var(arrId, new JST.ArrayLiteral())); innerBody.Add (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(methodArity - 1), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(Constants.arguments.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements(JST.Statement.DotCall (arrId.ToE(), Constants.push, new JST.IndexExpression(Constants.arguments.ToE(), iId.ToE()))))); innerArgs.Add(arrId.ToE()); } else { var innerArgId = gensym(); innerParameters.Add(innerArgId); innerArgs.Add(innerArgId.ToE()); } } if (ReturnType(methodDefn) == null) { innerBody.Add(JST.Statement.Call(funcArgId.ToE(), innerArgs)); innerBody.Add(new JST.ReturnStatement()); } else innerBody.Add(new JST.ReturnStatement(new JST.CallExpression(funcArgId.ToE(), innerArgs))); var innerFunction = new JST.FunctionExpression(innerParameters, new JST.Statements(innerBody)); callArgs.Add(innerFunction); } else callArgs.Add(funcArgId.ToE()); if (script is JST.FunctionExpression) body.Add(new JST.ExpressionStatement(new JST.CallExpression(script, callArgs))); else { var i = 0; if (bindToInstance) script = JST.Expression.Dot(callArgs[i++], JST.Expression.ExplodePath(script)); EnsurePathExists(body, script, !bindToInstance); body.Add(JST.Statement.Assignment(script, callArgs[i])); } function = new JST.FunctionExpression(parameters, new JST.Statements(body)); } return new ExportMethodInfo { MethodDefn = methodDefn, Script = function, BindToInstance = bindToInstance }; }
public MethodRef SelfMethodReference(TypeConstructorEnvironment tyconEnv) { var typeRef = tyconEnv.Type.SelfReference(tyconEnv); var methodBoundArguments = default(Seq<TypeRef>); if (TypeArity > 0) { methodBoundArguments = new Seq<TypeRef>(Arity); for (var i = 0; i < Arity; i++) methodBoundArguments.Add(new ParameterTypeRef(ParameterFlavor.Method, i)); } return PrimMethodReference(tyconEnv.Global, tyconEnv.Assembly, tyconEnv.Type, typeRef.Arguments, methodBoundArguments); }
public void Load(IImSeq<string> fileNames, out CCI.AssemblyNode mscorlib, out CCI.AssemblyNode jsTypes) { foreach (var fileName in fileNames) { var canonicalFileName = CanonicalFileName(fileName); if (fileNameToAssembly.ContainsKey(canonicalFileName)) { env.Log(new DuplicateAssemblyFileNameMessage(fileName, canonicalFileName)); throw new ExitException(); } else fileNameToAssembly.Add(canonicalFileName, null); } // ---------------------------------------- // Which assembly should we use for mscorlib and JSTypes? // ---------------------------------------- var mscorlibCanonicalName = default(string); var jsTypesCanonicalName = default(string); foreach (var kv in fileNameToAssembly) { var baseName = Path.GetFileNameWithoutExtension(kv.Key); if (baseName.ToLower().Contains(Constants.MsCorLibSimpleName.ToLower())) { if (mscorlibCanonicalName != null) { env.Log(new DuplicateSpecialAssemblyMessage(Constants.MsCorLibSimpleName, mscorlibCanonicalName, kv.Key)); throw new ExitException(); } mscorlibCanonicalName = kv.Key; } else if (baseName.ToLower().Contains(Constants.JSTypesSimpleName.ToLower())) { if (jsTypesCanonicalName != null) { env.Log(new DuplicateSpecialAssemblyMessage(Constants.JSTypesSimpleName, jsTypesCanonicalName, kv.Key)); throw new ExitException(); } jsTypesCanonicalName = kv.Key; } } if (mscorlibCanonicalName == null) { env.Log(new MissingSpecialAssemblyMessage(Constants.MsCorLibSimpleName)); throw new ExitException(); } if (jsTypesCanonicalName == null) { env.Log(new MissingSpecialAssemblyMessage(Constants.JSTypesSimpleName)); throw new ExitException(); } // ---------------------------------------- // Initialize CCI, which will implicitly load mscorlib // ---------------------------------------- var frameworkDir = Path.GetDirectoryName(mscorlibCanonicalName); if (!Directory.Exists(frameworkDir)) { env.Log(new UnloadableAssemblyMessage(frameworkDir, "directory does not exist")); throw new ExitException(); } // These special CCI assemblies, and mscorlib, will be picked up from the framework directory CCI.SystemDataAssemblyLocation.Location = null; CCI.SystemXmlAssemblyLocation.Location = null; CCI.TargetPlatform.SetToV2(frameworkDir); // At this point we could "fixup" CCI's hard-wired system assembly references: // // foreach (var asmRefs in CCI.TargetPlatform.AssemblyReferenceFor.GetEnumerator()) // { // var asmRef = (CCI.AssemblyReference)asmRefs.Value; // asmRef.Location = <the right place>; // } // SystemAssemblyLocation.Location = <the right place>; // SystemXmlAssemblyLocation.Location = <the right place>; // // But so far that doesn't seem necessary CCI.SystemTypes.Initialize(false, true, ResolveReference); // ---------------------------------------- // Account for mscorlib being loaded // ---------------------------------------- mscorlib = CCI.SystemTypes.SystemAssembly; if (mscorlib == null || mscorlib.Directory == null) { env.Log(new UnloadableAssemblyMessage(frameworkDir, "cannot load mscorlib")); throw new ExitException(); } env.Log(new FoundSpecialAssemblyMessage(Constants.MsCorLibSimpleName, mscorlib.StrongName)); fileNameToAssembly[mscorlibCanonicalName] = mscorlib; strongNameToInfo.Add (mscorlib.StrongName, new Info { Assembly = mscorlib, FileName = mscorlibCanonicalName }); // ---------------------------------------- // Load the remaining registered assemblies // ---------------------------------------- var pending = new Seq<string>(); foreach (var kv in fileNameToAssembly) { if (kv.Value == null) pending.Add(kv.Key); // else: must have been mscorlib, which we loaded above } jsTypes = null; foreach (var canonicalFileName in pending) { var assembly = CCI.AssemblyNode.GetAssembly(canonicalFileName, null, false, true, true); if (assembly == null) { env.Log(new UnloadableAssemblyMessage(canonicalFileName, "CCI cannot load assembly")); throw new ExitException(); } var info = default(Info); if (strongNameToInfo.TryGetValue(assembly.StrongName, out info)) { env.Log(new DuplicateAssemblyStrongNameMessage(canonicalFileName, assembly.StrongName, info.FileName)); throw new ExitException(); } fileNameToAssembly[canonicalFileName] = assembly; strongNameToInfo.Add(assembly.StrongName, new Info { Assembly = assembly, FileName = canonicalFileName }); assembly.AssemblyReferenceResolution += ResolveReference; if (canonicalFileName.Equals(jsTypesCanonicalName)) { jsTypes = assembly; env.Log(new FoundSpecialAssemblyMessage(Constants.JSTypesSimpleName, jsTypes.StrongName)); } } #if false // ---------------------------------------- // Check all references resolve to known definitions // ---------------------------------------- foreach (var kv in strongNameToInfo) { new CCI.StandardVisitor().Visit(kv.Value.Assembly); foreach (var reference in kv.Value.Assembly.AssemblyReferences) { if (reference.Assembly == null || !reference.Assembly.StrongName.Equals(reference.StrongName, StringComparison.OrdinalIgnoreCase) || reference.Assembly.Location.Equals("unknown:location", StringComparison.OrdinalIgnoreCase)) { env.Log(new UnresolvableReferenceMessage(kv.Key, kv.Value.FileName, reference.StrongName)); throw new ExitException(); } } foreach (var typeDefn in kv.Value.Assembly.Types) CheckTypeDefn(kv.Value.Assembly, typeDefn); env.Log(new LoadedAssemblyMessage(kv.Key, kv.Value.FileName)); } #endif if (loadFailed) throw new ExitException(); }
// Take account of: // - BindToPrototype // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private void AppendFinalExport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, JST.Expression instance, ISeq<JST.Statement> body, AppendCallExported appendCallExported) { if (script == null) throw new InvalidOperationException("expecting default script value"); var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); var isInstance = !methodDef.IsStatic && !methodDef.IsConstructor; if (isInstance) { if (typeDef.Style is CST.ValueTypeStyle) { env.Log(new InvalidInteropMessage(ctxt, "cannot export instance methods from value types")); throw new DefinitionException(); } } var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var isPassRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref isPassRoot); var isProto = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.TheBindToPrototypeProperty, true, false, ref isProto); var isPassInstance = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ExportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref isPassInstance); var bindToInstance = isInstance && !isProto; if (bindToInstance != (instance != null)) throw new InvalidOperationException("expecting instance"); var captureThis = isInstance && !isPassInstance; var funcScript = script as JST.FunctionExpression; // Build the function to export var funcBody = new Seq<JST.Statement>(); var funcParameters = new Seq<JST.Identifier>(); var funcCallArgs = new Seq<JST.Expression>(); var funcArity = methodDef.Arity; if ((methodDef.IsConstructor && !methodDef.IsStatic) || captureThis) // unmanaged will not pass the instance funcArity--; if (lastArgIsParamsArray) // managed params args will be extracted from remainder of unmanaged arguments funcArity--; if (captureThis) funcCallArgs.Add(new JST.ThisExpression()); for (var i = 0; i < funcArity; i++) { var id = nameSupply.GenSym(); funcParameters.Add(id); funcCallArgs.Add(id.ToE()); } if (lastArgIsParamsArray) { var iId = nameSupply.GenSym(); var arrId = nameSupply.GenSym(); funcBody.Add(JST.Statement.Var(arrId, new JST.ArrayLiteral())); funcBody.Add (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(funcArity), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(Constants.arguments.ToE(), Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (arrId.ToE(), Constants.push, new JST.IndexExpression(Constants.arguments.ToE(), iId.ToE()))))); funcCallArgs.Add(arrId.ToE()); } appendCallExported(nameSupply, assemblyDef, typeDef, methodDef, funcBody, funcCallArgs); var func = new JST.FunctionExpression(funcParameters, new JST.Statements(funcBody)); // Export the above function if (funcScript != null) { var scriptArgs = new Seq<JST.Expression>(); if (isPassRoot) scriptArgs.Add(rootId.ToE()); if (bindToInstance) scriptArgs.Add(instance); scriptArgs.Add(func); if (funcScript.Parameters.Count != scriptArgs.Count) { env.Log(new InvalidInteropMessage(ctxt, "invalid function arity")); throw new DefinitionException(); } body.Add(new JST.ExpressionStatement(new JST.CallExpression(script, scriptArgs))); } else { if (bindToInstance) script = JST.Expression.Dot(instance, JST.Expression.ExplodePath(script)); EnsurePathExists(body, script, !bindToInstance); body.Add(JST.Statement.Assignment(script, func)); } }
// Return all the exception clauses which: // - Start at given instruction index // - Are not in the current context // - Finish at the same instruction index // However: // - Only attempt to group catch clauses // Return null if no new exception clauses start at given instruction public ISeq<PE.ExceptionHandlingClause> OutermostTryBlocks(int i) { var offset = Instructions[i].Offset; if (TryOffsets.Contains(offset)) { // Clauses are in inner-to-outer order, look for the outermost var last = -1; for (var j = Handlers.Count - 1; j >= 0; j--) { var ehc = Handlers[j]; if (offset == ehc.TryOffset && !Within(ehc)) { last = j; break; } } if (last >= 0) { var res = new Seq<PE.ExceptionHandlingClause>(); var first = last - 1; while (first >= 0 && Handlers[first].TryOffset == Handlers[last].TryOffset && Handlers[first].TryLength == Handlers[last].TryLength && Handlers[first].Flags == PE.CorILExceptionClause.Exception && Handlers[last].Flags == PE.CorILExceptionClause.Exception) first--; first++; for (var j = first; j <= last; j++) res.Add(Handlers[j]); return res; } else return null; } else return null; }
// Take acccount of: // - PassRootAsArgument // - PassInstanceAsArgument // - InlineParamsArray private JST.Expression AppendFinalImport(JST.NameSupply nameSupply, JST.Identifier rootId, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef, JST.Expression script, ISeq<JST.Statement> body, IImSeq<JST.Expression> arguments) { var isInstanceMethod = !(methodDef.IsStatic || methodDef.IsConstructor); var scriptExpectsRoot = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.ThePassRootAsArgumentProperty, true, false, ref scriptExpectsRoot); var passInstAsArg = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.ThePassInstanceAsArgumentProperty, true, false, ref passInstAsArg); var instanceIsThis = isInstanceMethod && !passInstAsArg; var inlineParams = default(bool); attributeHelper.GetValueFromMethod (assemblyDef, typeDef, methodDef, attributeHelper.ImportAttributeRef, attributeHelper.TheInlineParamsArrayProperty, true, false, ref inlineParams); var lastArgIsParamsArray = methodDef.HasParamsArray(rootEnv) && inlineParams; var funcScript = script as JST.FunctionExpression; var nextArg = 0; var instArg = default(JST.Expression); if (instanceIsThis) { // Instance argument will be the first arg to 'call' or 'apply', or the target of a '.' call. instArg = arguments[nextArg++]; if (lastArgIsParamsArray && !instArg.IsDuplicatable) { // Make sure instance argument is evaluated before the remaining arguments var instId = nameSupply.GenSym(); body.Add(JST.Statement.Var(instId, instArg)); instArg = instId.ToE(); } } else { if (lastArgIsParamsArray) instArg = new JST.NullExpression(); } var knownArgs = 0; var call = default(JST.Expression); if (lastArgIsParamsArray) { // We mush build script args at runtime var argsId = nameSupply.GenSym(); body.Add(JST.Statement.Var(argsId, new JST.ArrayLiteral())); if (scriptExpectsRoot) { body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, rootId.ToE())); knownArgs++; } while (nextArg < arguments.Count - 1) { body.Add(JST.Statement.DotCall(argsId.ToE(), Constants.push, arguments[nextArg++])); knownArgs++; } var arrArg = arguments[nextArg]; if (!arrArg.IsDuplicatable) { var arrId = nameSupply.GenSym(); body.Add(JST.Statement.Var(arrId, arrArg)); arrArg = arrId.ToE(); } var iId = nameSupply.GenSym(); body.Add (new JST.IfStatement (JST.Expression.IsNotNull(arrArg), new JST.Statements (new JST.ForStatement (new JST.ForVarLoopClause (iId, new JST.NumericLiteral(0), new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(arrArg, Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)), new JST.Statements (JST.Statement.DotCall (argsId.ToE(), Constants.push, new JST.IndexExpression(arrArg, iId.ToE()))))))); if (funcScript != null) { // var args = ...; var x = script; x.apply(this/null, args) var scriptId = nameSupply.GenSym(); body.Add(JST.Statement.Var(scriptId, funcScript)); call = JST.Expression.DotCall(scriptId.ToE(), Constants.apply, instArg, argsId.ToE()); } else { if (instanceIsThis) { // var args = ...; (this.script).apply(this, args); call = JST.Expression.DotCall (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), Constants.apply, instArg, argsId.ToE()); } else { // var args = ...; script.apply(null, args) call = JST.Expression.DotCall(script, Constants.apply, instArg, argsId.ToE()); } } } else { var callArgs = new Seq<JST.Expression>(); if (instanceIsThis && funcScript != null) callArgs.Add(instArg); if (scriptExpectsRoot) { callArgs.Add(rootId.ToE()); knownArgs++; } while (nextArg < arguments.Count) { callArgs.Add(arguments[nextArg++]); knownArgs++; } if (instanceIsThis) { if (funcScript != null) { // var x = script; x.call(this, arg1, ..., argn) var scriptId = nameSupply.GenSym(); body.Add(JST.Statement.Var(scriptId, funcScript)); call = JST.Expression.DotCall(scriptId.ToE(), Constants.call, callArgs); } else // this.script(arg1, ..., angn) call = new JST.CallExpression (JST.Expression.Dot(instArg, JST.Expression.ExplodePath(script)), callArgs); } else // script(arg1, ..., argn) call = new JST.CallExpression(script, callArgs); } if (funcScript != null) { if (funcScript.Parameters.Count < knownArgs) { var ctxt = CST.MessageContextBuilders.Member(env.Global, assemblyDef, typeDef, methodDef); env.Log(new InvalidInteropMessage(ctxt, "script accepts too few arguments")); throw new DefinitionException(); } } return call; }
private Instructions InstructionsFromContext(TranslationContext ctxt) { var instructions = new Seq<Instruction>(); var i = ctxt.Start; while (!ctxt.AtEnd(i)) { var j = i > ctxt.Start ? ctxt.JumpOverHandlers(i) : i; if (j > i) i = j; else { var ehcs = ctxt.OutermostTryBlocks(i); if (ehcs != null) { // A try instruction begins here with given handlers var offset = ctxt.Instructions[i].Offset; var tryCtxt = new TryTranslationContext(ctxt, i, ehcs); var tryBlock = InstructionsFromContext(tryCtxt); var handlers = new Seq<TryInstructionHandler>(); foreach (var ehc in ehcs) { var handlerCtxt = new HandlerTranslationContext (tryCtxt, ctxt.OffsetToIndex[ehc.HandlerOffset], ehc); var handlerBlock = InstructionsFromContext(handlerCtxt); switch (ehc.Flags) { case PE.CorILExceptionClause.Exception: handlers.Add(new CatchTryInstructionHandler((TypeRef)ehc.Class, handlerBlock)); break; case PE.CorILExceptionClause.Filter: { var filterCtxt = new FilterTranslationContext (tryCtxt, ctxt.OffsetToIndex[ehc.FilterOffset], ehc); var filterBlock = InstructionsFromContext(filterCtxt); handlers.Add(new FilterTryInstructionHandler(filterBlock, handlerBlock)); break; } case PE.CorILExceptionClause.Finally: handlers.Add(new FinallyTryInstructionHandler(handlerBlock)); break; case PE.CorILExceptionClause.Fault: handlers.Add(new FaultTryInstructionHandler(handlerBlock)); break; default: throw new ArgumentOutOfRangeException(); } } instructions.Add(new TryInstruction(offset, tryBlock, handlers)); // Jump over try block var nextOffset = ehcs[0].TryOffset + ehcs[0].TryLength; if (!ctxt.OffsetToIndex.TryGetValue(nextOffset, out i)) i = ctxt.Instructions.Length; } else { var instruction = ctxt.Instructions[i++]; var offset = instruction.Offset; while (instruction.OpCode == PE.OpCode.Unaligned || instruction.OpCode == PE.OpCode.Volatile || instruction.OpCode == PE.OpCode.Tailcall) { // Skip over any ignored prefixes, but remember instruction begins at original offset // NOTE: What ever happened to the "no." prefix mentioned in the spec? if (i >= ctxt.Instructions.Length) throw new InvalidOperationException("invalid instructions"); instruction = ctxt.Instructions[i++]; } switch (instruction.OpCode) { case PE.OpCode.Cpblk: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Cpblk)); break; case PE.OpCode.Initblk: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Initblk)); break; case PE.OpCode.Arglist: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Arglist)); break; case PE.OpCode.Localloc: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Localloc)); break; case PE.OpCode.Jmp: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Jmp)); break; case PE.OpCode.Calli: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Calli)); break; case PE.OpCode.Sizeof: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Sizeof)); break; case PE.OpCode.Mkrefany: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Mkrefany)); break; case PE.OpCode.Refanytype: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Refanytype)); break; case PE.OpCode.Refanyval: instructions.Add(new UnsupportedInstruction(offset, UnsupportedOp.Refanyval)); break; case PE.OpCode.Nop: instructions.Add(new MiscInstruction(offset, MiscOp.Nop)); break; case PE.OpCode.Break: instructions.Add(new MiscInstruction(offset, MiscOp.Break)); break; case PE.OpCode.Dup: instructions.Add(new MiscInstruction(offset, MiscOp.Dup)); break; case PE.OpCode.Pop: instructions.Add(new MiscInstruction(offset, MiscOp.Pop)); break; case PE.OpCode.Ldnull: instructions.Add(new MiscInstruction(offset, MiscOp.Ldnull)); break; case PE.OpCode.Ckfinite: instructions.Add(new MiscInstruction(offset, MiscOp.Ckfinite)); break; case PE.OpCode.Throw: instructions.Add(new MiscInstruction(offset, MiscOp.Throw)); break; case PE.OpCode.Rethrow: instructions.Add(new MiscInstruction(offset, MiscOp.Rethrow)); break; case PE.OpCode.Ldind_ref: instructions.Add(new MiscInstruction(offset, MiscOp.LdindRef)); break; case PE.OpCode.Stind_ref: instructions.Add(new MiscInstruction(offset, MiscOp.StindRef)); break; case PE.OpCode.Ldelem_ref: instructions.Add(new MiscInstruction(offset, MiscOp.LdelemRef)); break; case PE.OpCode.Stelem_ref: instructions.Add(new MiscInstruction(offset, MiscOp.StelemRef)); break; case PE.OpCode.Ldlen: instructions.Add(new MiscInstruction(offset, MiscOp.Ldlen)); break; case PE.OpCode.Ret: if (ctxt.ResultType == null) instructions.Add(new MiscInstruction(offset, MiscOp.Ret)); else instructions.Add(new MiscInstruction(offset, MiscOp.RetVal)); break; case PE.OpCode.Endfilter: instructions.Add(new MiscInstruction(offset, MiscOp.Endfilter)); break; case PE.OpCode.Endfinally: // aka EndFault instructions.Add(new MiscInstruction(offset, MiscOp.Endfinally)); break; case PE.OpCode.Br_s: case PE.OpCode.Br: instructions.Add (new BranchInstruction(offset, BranchOp.Br, false, (int)instruction.Value)); break; case PE.OpCode.Brtrue_s: // aka brinst.s case PE.OpCode.Brtrue: // aka brinst instructions.Add (new BranchInstruction(offset, BranchOp.Brtrue, false, (int)instruction.Value)); break; case PE.OpCode.Brfalse_s: // aka brzero.s, brnull.s case PE.OpCode.Brfalse: // aka brzero, brnull instructions.Add (new BranchInstruction(offset, BranchOp.Brfalse, false, (int)instruction.Value)); break; case PE.OpCode.Beq: case PE.OpCode.Beq_s: instructions.Add (new BranchInstruction(offset, BranchOp.Breq, false, (int)instruction.Value)); break; case PE.OpCode.Bne_un: case PE.OpCode.Bne_un_s: instructions.Add (new BranchInstruction(offset, BranchOp.Brne, false, (int)instruction.Value)); break; case PE.OpCode.Leave: case PE.OpCode.Leave_s: instructions.Add (new BranchInstruction(offset, BranchOp.Leave, false, (int)instruction.Value)); break; case PE.OpCode.Blt: case PE.OpCode.Blt_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrLt, false, (int)instruction.Value)); break; case PE.OpCode.Blt_un: case PE.OpCode.Blt_un_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrLt, true, (int)instruction.Value)); break; case PE.OpCode.Ble: case PE.OpCode.Ble_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrLe, false, (int)instruction.Value)); break; case PE.OpCode.Ble_un: case PE.OpCode.Ble_un_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrLe, true, (int)instruction.Value)); break; case PE.OpCode.Bgt: case PE.OpCode.Bgt_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrGt, false, (int)instruction.Value)); break; case PE.OpCode.Bgt_un: case PE.OpCode.Bgt_un_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrGt, true, (int)instruction.Value)); break; case PE.OpCode.Bge: case PE.OpCode.Bge_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrGe, false, (int)instruction.Value)); break; case PE.OpCode.Bge_un: case PE.OpCode.Bge_un_s: instructions.Add (new BranchInstruction(offset, BranchOp.BrGe, true, (int)instruction.Value)); break; case PE.OpCode.Switch: instructions.Add(new SwitchInstruction(offset, (Seq<int>)instruction.Value)); break; case PE.OpCode.Ceq: instructions.Add(new CompareInstruction(offset, CompareOp.Ceq, false)); break; case PE.OpCode.Clt: instructions.Add(new CompareInstruction(offset, CompareOp.Clt, false)); break; case PE.OpCode.Clt_un: instructions.Add(new CompareInstruction(offset, CompareOp.Clt, true)); break; case PE.OpCode.Cgt: instructions.Add(new CompareInstruction(offset, CompareOp.Cgt, false)); break; case PE.OpCode.Cgt_un: instructions.Add(new CompareInstruction(offset, CompareOp.Cgt, true)); break; case PE.OpCode.Ldarg_0: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Arg, 0)); break; case PE.OpCode.Ldarg_1: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Arg, 1)); break; case PE.OpCode.Ldarg_2: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Arg, 2)); break; case PE.OpCode.Ldarg_3: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Arg, 3)); break; case PE.OpCode.Ldarg: case PE.OpCode.Ldarg_s: instructions.Add (new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Arg, (int)instruction.Value)); break; case PE.OpCode.Ldarga: case PE.OpCode.Ldarga_s: instructions.Add (new ArgLocalInstruction(offset, ArgLocalOp.Lda, ArgLocal.Arg, (int)instruction.Value)); break; case PE.OpCode.Starg: case PE.OpCode.Starg_s: instructions.Add (new ArgLocalInstruction(offset, ArgLocalOp.St, ArgLocal.Arg, (int)instruction.Value)); break; case PE.OpCode.Ldloc_0: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Local, 0)); break; case PE.OpCode.Ldloc_1: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Local, 1)); break; case PE.OpCode.Ldloc_2: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Local, 2)); break; case PE.OpCode.Ldloc_3: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.Ld, ArgLocal.Local, 3)); break; case PE.OpCode.Ldloc: case PE.OpCode.Ldloc_s: instructions.Add (new ArgLocalInstruction (offset, ArgLocalOp.Ld, ArgLocal.Local, (int)instruction.Value)); break; case PE.OpCode.Ldloca: case PE.OpCode.Ldloca_s: instructions.Add (new ArgLocalInstruction (offset, ArgLocalOp.Lda, ArgLocal.Local, (int)instruction.Value)); break; case PE.OpCode.Stloc_0: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.St, ArgLocal.Local, 0)); break; case PE.OpCode.Stloc_1: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.St, ArgLocal.Local, 1)); break; case PE.OpCode.Stloc_2: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.St, ArgLocal.Local, 2)); break; case PE.OpCode.Stloc_3: instructions.Add(new ArgLocalInstruction(offset, ArgLocalOp.St, ArgLocal.Local, 3)); break; case PE.OpCode.Stloc: case PE.OpCode.Stloc_s: instructions.Add (new ArgLocalInstruction (offset, ArgLocalOp.St, ArgLocal.Local, (int)instruction.Value)); break; case PE.OpCode.Ldfld: instructions.Add (new FieldInstruction(offset, FieldOp.Ldfld, (FieldRef)instruction.Value, false)); break; case PE.OpCode.Ldsfld: instructions.Add (new FieldInstruction(offset, FieldOp.Ldfld, (FieldRef)instruction.Value, true)); break; case PE.OpCode.Ldflda: instructions.Add (new FieldInstruction(offset, FieldOp.Ldflda, (FieldRef)instruction.Value, false)); break; case PE.OpCode.Ldsflda: instructions.Add (new FieldInstruction(offset, FieldOp.Ldflda, (FieldRef)instruction.Value, true)); break; case PE.OpCode.Stfld: instructions.Add (new FieldInstruction(offset, FieldOp.Stfld, (FieldRef)instruction.Value, false)); break; case PE.OpCode.Stsfld: instructions.Add (new FieldInstruction(offset, FieldOp.Stfld, (FieldRef)instruction.Value, true)); break; case PE.OpCode.Ldtoken: { if (instruction.Value is FieldRef) instructions.Add (new FieldInstruction (offset, FieldOp.Ldtoken, (FieldRef)instruction.Value, default(bool))); else if (instruction.Value is MethodRef) instructions.Add (new MethodInstruction (offset, MethodOp.Ldtoken, null, false, (MethodRef)instruction.Value)); else if (instruction.Value is TypeRef) // NOTE: May be a higher-kinded type instructions.Add (new TypeInstruction(offset, TypeOp.Ldtoken, (TypeRef)instruction.Value)); else throw new InvalidOperationException("unexpected ldtoken instruction value"); break; } case PE.OpCode.Constrained: { var constrained = (TypeRef)instruction.Value; if (i >= ctxt.Instructions.Length) throw new InvalidOperationException("invalid instructions"); instruction = ctxt.Instructions[i++]; if (instruction.OpCode != PE.OpCode.Callvirt) throw new InvalidOperationException("invalid instruction"); instructions.Add (new MethodInstruction (offset, MethodOp.Call, constrained, true, (MethodRef)instruction.Value)); break; } case PE.OpCode.Call: instructions.Add (new MethodInstruction (offset, MethodOp.Call, null, false, (MethodRef)instruction.Value)); break; case PE.OpCode.Callvirt: instructions.Add (new MethodInstruction (offset, MethodOp.Call, null, true, (MethodRef)instruction.Value)); break; case PE.OpCode.Ldftn: instructions.Add (new MethodInstruction (offset, MethodOp.Ldftn, null, false, (MethodRef)instruction.Value)); break; case PE.OpCode.Ldvirtftn: instructions.Add (new MethodInstruction (offset, MethodOp.Ldftn, null, true, (MethodRef)instruction.Value)); break; case PE.OpCode.Newobj: instructions.Add (new MethodInstruction (offset, MethodOp.Newobj, null, false, (MethodRef)instruction.Value)); break; case PE.OpCode.Ldind_i1: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.Int8Ref)); break; case PE.OpCode.Ldind_u1: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.UInt8Ref)); break; case PE.OpCode.Ldind_i2: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.Int16Ref)); break; case PE.OpCode.Ldind_u2: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.UInt16Ref)); break; case PE.OpCode.Ldind_i4: case PE.OpCode.Ldind_u4: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.Int32Ref)); break; case PE.OpCode.Ldind_i8: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.Int64Ref)); break; case PE.OpCode.Ldind_i: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.IntNativeRef)); break; case PE.OpCode.Ldind_r4: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.SingleRef)); break; case PE.OpCode.Ldind_r8: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, global.DoubleRef)); break; case PE.OpCode.Ldobj: instructions.Add(new TypeInstruction(offset, TypeOp.Ldobj, (TypeRef)instruction.Value)); break; case PE.OpCode.Stind_i1: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.Int8Ref)); break; case PE.OpCode.Stind_i2: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.Int16Ref)); break; case PE.OpCode.Stind_i4: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.Int32Ref)); break; case PE.OpCode.Stind_i8: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.Int64Ref)); break; case PE.OpCode.Stind_i: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.IntNativeRef)); break; case PE.OpCode.Stind_r4: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.SingleRef)); break; case PE.OpCode.Stind_r8: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, global.DoubleRef)); break; case PE.OpCode.Stobj: instructions.Add(new TypeInstruction(offset, TypeOp.Stobj, (TypeRef)instruction.Value)); break; case PE.OpCode.Cpobj: instructions.Add(new TypeInstruction(offset, TypeOp.Cpobj, (TypeRef)instruction.Value)); break; case PE.OpCode.Newarr: instructions.Add(new TypeInstruction(offset, TypeOp.Newarr, (TypeRef)instruction.Value)); break; case PE.OpCode.Initobj: instructions.Add(new TypeInstruction(offset, TypeOp.Initobj, (TypeRef)instruction.Value)); break; case PE.OpCode.Castclass: instructions.Add (new TypeInstruction(offset, TypeOp.Castclass, (TypeRef)instruction.Value)); break; case PE.OpCode.Isinst: instructions.Add(new TypeInstruction(offset, TypeOp.Isinst, (TypeRef)instruction.Value)); break; case PE.OpCode.Box: instructions.Add(new TypeInstruction(offset, TypeOp.Box, (TypeRef)instruction.Value)); break; case PE.OpCode.Unbox: instructions.Add(new TypeInstruction(offset, TypeOp.Unbox, (TypeRef)instruction.Value)); break; case PE.OpCode.Unbox_any: instructions.Add(new TypeInstruction(offset, TypeOp.UnboxAny, (TypeRef)instruction.Value)); break; case PE.OpCode.Ldelem_i1: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.Int8Ref)); break; case PE.OpCode.Ldelem_u1: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.UInt8Ref)); break; case PE.OpCode.Ldelem_i2: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.Int16Ref)); break; case PE.OpCode.Ldelem_u2: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.UInt16Ref)); break; case PE.OpCode.Ldelem_i4: case PE.OpCode.Ldelem_u4: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.Int32Ref)); break; case PE.OpCode.Ldelem_i: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.IntNativeRef)); break; case PE.OpCode.Ldelem_i8: // aka ldelem.u8 instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.Int64Ref)); break; case PE.OpCode.Ldelem_r4: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.SingleRef)); break; case PE.OpCode.Ldelem_r8: instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, global.DoubleRef)); break; case PE.OpCode.Ldelem: // aka ldelem.any instructions.Add(new TypeInstruction(offset, TypeOp.Ldelem, (TypeRef)instruction.Value)); break; case PE.OpCode.Stelem_i1: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.Int8Ref)); break; case PE.OpCode.Stelem_i2: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.Int16Ref)); break; case PE.OpCode.Stelem_i4: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.Int32Ref)); break; case PE.OpCode.Stelem_i8: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.Int64Ref)); break; case PE.OpCode.Stelem_i: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.IntNativeRef)); break; case PE.OpCode.Stelem_r4: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.SingleRef)); break; case PE.OpCode.Stelem_r8: instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, global.DoubleRef)); break; case PE.OpCode.Stelem: // aka stelem.any instructions.Add(new TypeInstruction(offset, TypeOp.Stelem, (TypeRef)instruction.Value)); break; case PE.OpCode.Readonly: if (i >= ctxt.Instructions.Length) throw new InvalidOperationException("invalid instruction"); instruction = ctxt.Instructions[i++]; if (instruction.OpCode != PE.OpCode.Ldelema) throw new InvalidOperationException("invalid instruction"); instructions.Add(new LdElemAddrInstruction(offset, true, (TypeRef)instruction.Value)); break; case PE.OpCode.Ldelema: instructions.Add(new LdElemAddrInstruction(offset, false, (TypeRef)instruction.Value)); break; case PE.OpCode.Ldc_i4_0: instructions.Add(new LdInt32Instruction(offset, 0)); break; case PE.OpCode.Ldc_i4_1: instructions.Add(new LdInt32Instruction(offset, 1)); break; case PE.OpCode.Ldc_i4_2: instructions.Add(new LdInt32Instruction(offset, 2)); break; case PE.OpCode.Ldc_i4_3: instructions.Add(new LdInt32Instruction(offset, 3)); break; case PE.OpCode.Ldc_i4_4: instructions.Add(new LdInt32Instruction(offset, 4)); break; case PE.OpCode.Ldc_i4_5: instructions.Add(new LdInt32Instruction(offset, 5)); break; case PE.OpCode.Ldc_i4_6: instructions.Add(new LdInt32Instruction(offset, 6)); break; case PE.OpCode.Ldc_i4_7: instructions.Add(new LdInt32Instruction(offset, 7)); break; case PE.OpCode.Ldc_i4_8: instructions.Add(new LdInt32Instruction(offset, 8)); break; case PE.OpCode.Ldc_i4_m1: instructions.Add(new LdInt32Instruction(offset, -1)); break; case PE.OpCode.Ldc_i4: case PE.OpCode.Ldc_i4_s: instructions.Add(new LdInt32Instruction(offset, (int)instruction.Value)); break; case PE.OpCode.Ldc_i8: instructions.Add(new LdInt64Instruction(offset, (long)instruction.Value)); break; case PE.OpCode.Ldc_r4: instructions.Add(new LdSingleInstruction(offset, (float)instruction.Value)); break; case PE.OpCode.Ldc_r8: instructions.Add(new LdDoubleInstruction(offset, (double)instruction.Value)); break; case PE.OpCode.Ldstr: instructions.Add(new LdStringInstruction(offset, (string)instruction.Value)); break; case PE.OpCode.Add: instructions.Add(new ArithInstruction(offset, ArithOp.Add, false, false)); break; case PE.OpCode.Add_ovf: instructions.Add(new ArithInstruction(offset, ArithOp.Add, true, false)); break; case PE.OpCode.Add_ovf_un: instructions.Add(new ArithInstruction(offset, ArithOp.Add, true, true)); break; case PE.OpCode.Sub: instructions.Add(new ArithInstruction(offset, ArithOp.Sub, false, false)); break; case PE.OpCode.Sub_ovf: instructions.Add(new ArithInstruction(offset, ArithOp.Sub, true, false)); break; case PE.OpCode.Sub_ovf_un: instructions.Add(new ArithInstruction(offset, ArithOp.Sub, true, true)); break; case PE.OpCode.Mul: instructions.Add(new ArithInstruction(offset, ArithOp.Mul, false, false)); break; case PE.OpCode.Mul_ovf: instructions.Add(new ArithInstruction(offset, ArithOp.Mul, true, false)); break; case PE.OpCode.Mul_ovf_un: instructions.Add(new ArithInstruction(offset, ArithOp.Mul, true, true)); break; case PE.OpCode.Div: instructions.Add(new ArithInstruction(offset, ArithOp.Div, false, false)); break; case PE.OpCode.Div_un: instructions.Add(new ArithInstruction(offset, ArithOp.Div, false, true)); break; case PE.OpCode.Rem: instructions.Add(new ArithInstruction(offset, ArithOp.Rem, false, false)); break; case PE.OpCode.Rem_un: instructions.Add(new ArithInstruction(offset, ArithOp.Rem, false, true)); break; case PE.OpCode.Neg: instructions.Add(new ArithInstruction(offset, ArithOp.Neg, false, false)); break; case PE.OpCode.And: instructions.Add(new ArithInstruction(offset, ArithOp.BitAnd, false, false)); break; case PE.OpCode.Or: instructions.Add(new ArithInstruction(offset, ArithOp.BitOr, false, false)); break; case PE.OpCode.Xor: instructions.Add(new ArithInstruction(offset, ArithOp.BitXor, false, false)); break; case PE.OpCode.Not: instructions.Add(new ArithInstruction(offset, ArithOp.BitNot, false, false)); break; case PE.OpCode.Shl: instructions.Add(new ArithInstruction(offset, ArithOp.Shl, false, false)); break; case PE.OpCode.Shr: instructions.Add(new ArithInstruction(offset, ArithOp.Shr, false, false)); break; case PE.OpCode.Shr_un: instructions.Add(new ArithInstruction(offset, ArithOp.Shr, false, true)); break; case PE.OpCode.Conv_i1: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int8, false, false)); break; case PE.OpCode.Conv_u1: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt8, false, false)); break; case PE.OpCode.Conv_i2: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int16, false, false)); break; case PE.OpCode.Conv_u2: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt16, false, false)); break; case PE.OpCode.Conv_i4: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int32, false, false)); break; case PE.OpCode.Conv_u4: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt32, false, false)); break; case PE.OpCode.Conv_i8: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int64, false, false)); break; case PE.OpCode.Conv_u8: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt64, false, false)); break; case PE.OpCode.Conv_i: instructions.Add(new ConvInstruction(offset, NumberFlavor.IntNative, false, false)); break; case PE.OpCode.Conv_u: instructions.Add(new ConvInstruction(offset, NumberFlavor.UIntNative, false, false)); break; case PE.OpCode.Conv_r4: instructions.Add(new ConvInstruction(offset, NumberFlavor.Single, false, false)); break; case PE.OpCode.Conv_r8: instructions.Add(new ConvInstruction(offset, NumberFlavor.Double, false, false)); break; case PE.OpCode.Conv_r_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.Double, false, true)); break; case PE.OpCode.Conv_ovf_i1: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int8, true, false)); break; case PE.OpCode.Conv_ovf_u1: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt8, true, false)); break; case PE.OpCode.Conv_ovf_i2: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int16, true, false)); break; case PE.OpCode.Conv_ovf_u2: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt16, true, false)); break; case PE.OpCode.Conv_ovf_i4: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int32, true, false)); break; case PE.OpCode.Conv_ovf_u4: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt32, true, false)); break; case PE.OpCode.Conv_ovf_i8: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int64, true, false)); break; case PE.OpCode.Conv_ovf_u8: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt64, true, false)); break; case PE.OpCode.Conv_ovf_i: instructions.Add(new ConvInstruction(offset, NumberFlavor.IntNative, true, false)); break; case PE.OpCode.Conv_ovf_u: instructions.Add(new ConvInstruction(offset, NumberFlavor.UIntNative, true, false)); break; case PE.OpCode.Conv_ovf_i1_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int8, true, true)); break; case PE.OpCode.Conv_ovf_u1_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt8, true, true)); break; case PE.OpCode.Conv_ovf_i2_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int16, true, true)); break; case PE.OpCode.Conv_ovf_u2_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt16, true, true)); break; case PE.OpCode.Conv_ovf_i4_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int32, true, true)); break; case PE.OpCode.Conv_ovf_u4_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt32, true, true)); break; case PE.OpCode.Conv_ovf_i8_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.Int64, true, true)); break; case PE.OpCode.Conv_ovf_u8_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.UInt64, true, true)); break; case PE.OpCode.Conv_ovf_i_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.IntNative, true, true)); break; case PE.OpCode.Conv_ovf_u_un: instructions.Add(new ConvInstruction(offset, NumberFlavor.UIntNative, true, true)); break; default: throw new InvalidOperationException("invalid instruction"); } } } } // Must always contain at least one instruction, otherwise control would have fallen through if (instructions.Count == 0) throw new InvalidOperationException("empty instructions"); return new Instructions(null, instructions); }
private Location Statement(Seq<Statement> statements, bool isTop) { ConsumeCommentStatement(statements); switch (Current.Tag) { case InputElementTag.LBrace: { return BlockStatements("block", false, statements); } case InputElementTag.Var: { var variableDeclarations = new Seq<VariableDeclaration>(); var loc = VariableDeclarations(variableDeclarations); statements.Add(new VariableStatement(loc, variableDeclarations)); return loc; } case InputElementTag.Semicolon: { var loc = Current.Loc; Consume(); return loc; } case InputElementTag.If: { var loc = Current.Loc; Consume(); Only("if statement", "'('", InputElementTag.LParen); var condition = Expression(false); Only("if statement", "')'", InputElementTag.RParen); condition = ConsumeCommentExpression(condition); var thenStatements = new Seq<Statement>(); loc = loc.Union(Statement(thenStatements, false)); if (Current.Tag == InputElementTag.Else) { var elseStatements = new Seq<Statement>(); Consume(); loc = loc.Union(Statement(elseStatements, false)); statements.Add(new IfStatement(loc, condition, new Statements(thenStatements), new Statements(elseStatements))); } else statements.Add(new IfStatement(loc, condition, new Statements(thenStatements))); return loc; } case InputElementTag.Do: { var loc = Current.Loc; Consume(); var body = new Seq<Statement>(); Statement(body, false); Only("do statement", "'while'", InputElementTag.While); Only("do statement", "'('", InputElementTag.LParen); var condition = Expression(false); Only("do statement", "')'", InputElementTag.RParen); condition = ConsumeCommentExpression(condition); OptSemicolon("do statement"); loc = loc.Union(condition.Loc); statements.Add(new DoStatement(loc, new Statements(body), condition)); return loc; } case InputElementTag.While: { var loc = Current.Loc; Consume(); Only("while statement", "'('", InputElementTag.LParen); var condition = Expression(false); Only("while statement", "')'", InputElementTag.RParen); condition = ConsumeCommentExpression(condition); var body = new Seq<Statement>(); loc = loc.Union(Statement(body, false)); statements.Add(new WhileStatement(loc, condition, new Statements(body))); return loc; } case InputElementTag.For: { var loc = Current.Loc; Consume(); var loopClause = LoopClause(); var body = new Seq<Statement>(); loc = loc.Union(Statement(body, false)); statements.Add(new ForStatement(loc, loopClause, new Statements(body))); return loc; } case InputElementTag.Continue: { var loc = Current.Loc; Consume(); // ask for Current to make sure lastLineTerminator is updates var isid = Current.Tag == InputElementTag.Identifier; var label = default(Identifier); if (lastLineTerminator == null) { if (isid) { label = new Identifier(Current.Loc, Current.Value); loc = loc.Union(label.Loc); Consume(); } OptSemicolon("continue statement"); } statements.Add(new ContinueStatement(loc, label)); return loc; } case InputElementTag.Break: { var loc = Current.Loc; Consume(); // ask for Current to make sure lastLineTerminator is updates var isid = Current.Tag == InputElementTag.Identifier; var label = default(Identifier); if (lastLineTerminator == null) { if (isid) { label = new Identifier(Current.Loc, Current.Value); loc = loc.Union(label.Loc); Consume(); } OptSemicolon("break statement"); } statements.Add(new BreakStatement(loc, label)); return loc; } case InputElementTag.Return: { var loc = Current.Loc; Consume(); var value = default(Expression); // ask for Current to make sure lastLineTerminator is updates var isExpr = IsExpression(); if (lastLineTerminator == null) { if (isExpr) { value = Expression(false); loc = loc.Union(value.Loc); } OptSemicolon("return statement"); if (value != null) value = ConsumeCommentExpression(value); } statements.Add(new ReturnStatement(loc, value)); return loc; } case InputElementTag.With: { var loc = Current.Loc; Consume(); Only("with statement", "'('", InputElementTag.LParen); var environment = Expression(false); Only("with statement", "')'", InputElementTag.RParen); environment = ConsumeCommentExpression(environment); var body = new Seq<Statement>(); loc = loc.Union(Statement(body, false)); statements.Add(new WithStatement(loc, environment, new Statements(body))); return loc; } case InputElementTag.Identifier: { var loc = Current.Loc; var ie = Current; Consume(); if (Current.Tag == InputElementTag.Colon) { var label = new Identifier(ie.Loc, ie.Value); Consume(); var body = new Seq<Statement>(); loc = loc.Union(Statement(body, false)); statements.Add(new LabelledStatement(loc, label, new Statements(body))); } else { Regurgitate(ie); var expression = Expression(false); loc = expression.Loc; OptSemicolon("expression statement"); expression = ConsumeCommentExpression(expression); statements.Add(new ExpressionStatement(loc, expression)); } return loc; } case InputElementTag.Switch: { var loc = Current.Loc; Consume(); Only("switch statement", "'('", InputElementTag.LParen); var value = Expression(false); Only("switch statement", "')'", InputElementTag.RParen); value = ConsumeCommentExpression(value); Only("switch statement", "'{'", InputElementTag.LBrace); var cases = new Seq<CaseClause>(); var defaultc = default(DefaultClause); while (Current.Tag != InputElementTag.RBrace) { if (Current.Tag == InputElementTag.Case) { var caseLoc = Current.Loc; Consume(); var caseValue = Expression(false); Only("case clause", "':'", InputElementTag.Colon); var caseBody = new Seq<Statement>(); while (Current.Tag != InputElementTag.Case && Current.Tag != InputElementTag.Default && Current.Tag != InputElementTag.RBrace) caseLoc = caseLoc.Union(Statement(caseBody, false)); ConsumeCommentStatement(caseBody); cases.Add(new CaseClause(caseLoc, caseValue, new Statements(caseBody))); } else if (Current.Tag == InputElementTag.Default) { if (defaultc != null) throw MsgError ("case clause", String.Format("default clause already present at {0}", defaultc.Loc)); var defaultLoc = Current.Loc; Consume(); Only("default clause", "':'", InputElementTag.Colon); var defaultBody = new Seq<Statement>(); while (Current.Tag != InputElementTag.Case && Current.Tag != InputElementTag.Default && Current.Tag != InputElementTag.RBrace) defaultLoc = defaultLoc.Union(Statement(defaultBody, false)); ConsumeCommentStatement(defaultBody); defaultc = new DefaultClause(defaultLoc, new Statements(defaultBody), cases.Count); } else throw IEError("switch statement", "'case' or 'default' or '}'"); } loc = loc.Union(Current.Loc); Consume(); statements.Add(new SwitchStatement(loc, value, cases, defaultc)); return loc; } case InputElementTag.Throw: { var loc = Current.Loc; Consume(); // ask for Current to make sure lastLineTerminator is updates var dummy = Current; var value = default(Expression); if (lastLineTerminator == null) { value = Expression(false); OptSemicolon("throw statement"); value = ConsumeCommentExpression(value); } else { value = Expression(false); value = ConsumeCommentExpression(value); } loc = loc.Union(value.Loc); statements.Add(new ThrowStatement(loc, value)); return loc; } case InputElementTag.Try: { var loc = Current.Loc; Consume(); var tryStatements = new Seq<Statement>(); BlockStatements("try statement", false, tryStatements); var catchc = default(CatchClause); var finallyc = default(FinallyClause); if (Current.Tag == InputElementTag.Catch) { var catchLoc = Current.Loc; Consume(); Only("catch clause", "'('", InputElementTag.LParen); if (Current.Tag != InputElementTag.Identifier) throw IEError("catch clasue", "identifier"); var name = new Identifier(Current.Loc, Current.Value); Consume(); Only("catch clause", "')'", InputElementTag.RParen); var catchBody = new Seq<Statement>(); catchLoc = catchLoc.Union(BlockStatements("catch clause", false, catchBody)); catchc = new CatchClause(catchLoc, name, new Statements(catchBody)); loc = loc.Union(catchLoc); } if (Current.Tag == InputElementTag.Finally) { var finallyLoc = Current.Loc; Consume(); var finallyBody = new Seq<Statement>(); finallyLoc = finallyLoc.Union(BlockStatements("finally clause", false, finallyBody)); finallyc = new FinallyClause(finallyLoc, new Statements(finallyBody)); loc = loc.Union(finallyLoc); } else if (catchc == null) throw IEError("try statement", "'catch' or 'finally'"); statements.Add(new TryStatement(loc, new Statements(tryStatements), catchc, finallyc)); return loc; } case InputElementTag.Function: { if (lexer.IsStrict && !isTop) throw MsgError("statement", "function declarations not permitted in nested blocks"); var loc = Current.Loc; Consume(); if (Current.Tag != InputElementTag.Identifier) throw IEError("function declaration", "identifier"); var name = new Identifier(Current.Loc, Current.Value); Consume(); if (Current.Tag != InputElementTag.LParen) throw IEError("function declaration", "'('"); var parameters = new Seq<Identifier>(); DelimitedList ("function declaration parameters", "',' or ')'", InputElementTag.Comma, InputElementTag.RParen, () => { if (Current.Tag != InputElementTag.Identifier) throw IEError("function declaration parameters", "identifier"); parameters.Add(new Identifier(Current.Loc, Current.Value)); Consume(); }); var body = new Seq<Statement>(); loc = loc.Union(BlockStatements("function declaration", true, body)); statements.Add(new FunctionDeclaration(loc, name, parameters, new Statements(body))); return loc; } default: { var expression = Expression(false); var loc = expression.Loc; OptSemicolon("expression statement"); expression = ConsumeCommentExpression(expression); statements.Add(new ExpressionStatement(loc, expression)); return loc; } } }
private Expression PrimaryExpression() { switch (Current.Tag) { case InputElementTag.This: { var loc = Current.Loc; Consume(); return new ThisExpression(loc); } case InputElementTag.Debugger: { var loc = Current.Loc; Consume(); return new DebuggerExpression(loc); } case InputElementTag.Identifier: { var id = new Identifier(Current.Loc, Current.Value); Consume(); return new IdentifierExpression(id.Loc, id); } case InputElementTag.Number: { var nl = NumericLiteral.FromJavaScript(Current.Loc, Current.Value); Consume(); return nl; } case InputElementTag.String: { var sl = new StringLiteral(Current.Loc, Current.Value); // lexer has already converted to underlying value Consume(); return sl; } case InputElementTag.Null: { var loc = Current.Loc; Consume(); return new NullExpression(Current.Loc); } case InputElementTag.True: case InputElementTag.False: { var bl = BooleanLiteral.FromJavaScript(Current.Loc, Current.Value); Consume(); return bl; } case InputElementTag.Regexp: { // lexer has NOT already converted to underlying value var rl = RegularExpressionLiteral.FromJavaScript(Current.Loc, Current.Value); Consume(); return rl; } case InputElementTag.LSquare: { var loc = Current.Loc; var elems = new Seq<Expression>(); Consume(); while (Current.Tag != InputElementTag.RSquare) { if (Current.Tag == InputElementTag.Comma) { var elem = new IdentifierExpression (Current.Loc, new Identifier(Current.Loc, Identifier.Undefined.Value)); elems.Add(elem); Consume(); } else { var elem = AssignmentExpression(false); elems.Add(elem); if (Current.Tag == InputElementTag.Comma) Consume(); else if (Current.Tag != InputElementTag.RSquare) throw IEError("array literal", "',' or ']'"); } } loc = loc.Union(Current.Loc); Consume(); return new ArrayLiteral(loc, elems); } case InputElementTag.LBrace: { var bindings = new OrdMap<PropertyName, Expression>(); var loc = DelimitedList ("object literal", "',' or '}'", InputElementTag.Comma, InputElementTag.RBrace, () => { var propName = default(PropertyName); switch (Current.Tag) { case InputElementTag.Identifier: propName = new PropertyName(Current.Loc, Current.Value); break; case InputElementTag.String: propName = new PropertyName(Current.Loc, Current.Value); break; case InputElementTag.Number: propName = PropertyName.FromJavaScriptNumber(Current.Loc, Current.Value); break; default: throw IEError("object literal", "identifier, string or number"); } Consume(); Only("object literal", "':'", InputElementTag.Colon); var value = AssignmentExpression(false); // This will silently ignore repeat bindings bindings.Add(propName, value); }); LastWasInExpressionContext(); return new ObjectLiteral(loc, bindings); } case InputElementTag.LParen: { Consume(); var e = Expression(false); Only("expression", "')'", InputElementTag.RParen); LastWasInExpressionContext(); return e; } case InputElementTag.Function: { var loc = Current.Loc; Consume(); var name = default(Identifier); if (Current.Tag == InputElementTag.Identifier) { name = new Identifier(Current.Loc, Current.Value); Consume(); } if (Current.Tag != InputElementTag.LParen) throw IEError("function expression", "'('"); var parameters = new Seq<Identifier>(); DelimitedList ("function expression parameters", "',' or ')'", InputElementTag.Comma, InputElementTag.RParen, () => { if (Current.Tag != InputElementTag.Identifier) throw IEError("function expression parameters", "identifier"); parameters.Add(new Identifier(Current.Loc, Current.Value)); Consume(); }); var body = new Seq<Statement>(); loc = loc.Union(BlockStatements("function expression", true, body)); return new FunctionExpression(loc, name, parameters, new Statements(body)); } default: throw IEError("expression", "primary expression"); } }
// ---------------------------------------------------------------------- // Methods // ---------------------------------------------------------------------- // Emit bindings for static or instance methods, but not for virtuals or interface methods // - Invoked from TypeDefinitionCompiler for higher-kinded type definitions // - Invoked from TypeCompiler for first-kinded type definitions public void EmitMethods(Seq<JST.Statement> body, JST.Expression lhs, JST.NameSupply outerNameSupply, JST.Expression target, bool isStatic) { switch (Env.CompilationMode) { case CompilationMode.Plain: { // Method definitions are bound directly into target foreach (var methodDef in Methods.Where(m => m.Invalid == null)) { if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic) { var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, target); } } break; } case CompilationMode.Collecting: { // Method definitions are bound into MethodCache, redirectors are bound into target foreach (var methodDef in Methods.Where(m => m.Invalid == null)) { if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic) { var slot = Env.GlobalMapping.ResolveMethodDefToSlot(TyconEnv.Assembly, TyconEnv.Type, methodDef); var methodName = CST.CSTWriter.WithAppend (Env.Global, CST.WriterStyle.Uniform, methodDef.MethodSignature.Append); body.Add (JST.Statement.DotCall (RootId.ToE(), Constants.RootCollectingBindMethodBuilder, lhs, new JST.BooleanLiteral(isStatic), new JST.StringLiteral(slot), new JST.StringLiteral(methodName))); var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, JST.Expression.Dot(target, Constants.TypeMethodCache)); } } break; } case CompilationMode.Traced: { // Methods in the initial trace or this trace will be bound directly. // Methods in a trace other than above are bound via builder which is given trace name. // Remaining methods are built via builder with null trace name. var traceToArgs = new Map<string, Seq<JST.Expression>>(); var remainingArgs = new Seq<JST.Expression>(); remainingArgs.Add(TypeDefinitionId.ToE()); remainingArgs.Add(new JST.BooleanLiteral(isStatic)); remainingArgs.Add(new JST.NullExpression()); foreach (var methodDef in Methods.Where(m => m.Invalid == null)) { if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic) { var slot = Env.GlobalMapping.ResolveMethodDefToSlot(TyconEnv.Assembly, TyconEnv.Type, methodDef); var defTrace = Env.Traces.MethodToTrace[methodDef.QualifiedMemberName(Env.Global, TyconEnv.Assembly, TyconEnv.Type)]; if (defTrace.Flavor == TraceFlavor.OnDemand && defTrace != TypeTrace.Parent.Parent) { // Method definition in in another trace, bind redirector for it. var args = default(Seq<JST.Expression>); if (!traceToArgs.TryGetValue(defTrace.Name, out args)) { args = new Seq<JST.Expression>(); args.Add(lhs); args.Add(new JST.BooleanLiteral(isStatic)); args.Add(new JST.StringLiteral(defTrace.Name)); traceToArgs.Add(defTrace.Name, args); } args.Add(new JST.StringLiteral(slot)); } else if (defTrace.Flavor == TraceFlavor.Remainder) // Method definition is in a stand-alone loader, bind redirector for it. remainingArgs.Add(new JST.StringLiteral(slot)); else { // Method definition is bound directly var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind); compiler.Emit(body, target); } } } foreach (var kv in traceToArgs) body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootBindMethodBuilders, kv.Value)); if (remainingArgs.Count > 3) body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootBindMethodBuilders, remainingArgs)); break; } default: throw new ArgumentOutOfRangeException(); } }
private Expression LeftHandSideFollow(Seq<InputElement> newStack, Expression lhs) { switch (Current.Tag) { case InputElementTag.LParen: { var applicand = lhs; var arguments = new Seq<Expression>(); var loc = DelimitedList("call expression arguments", "')' or ','", InputElementTag.Comma, InputElementTag.RParen, () => arguments.Add(AssignmentExpression(false))); LastWasInExpressionContext(); var ce = new CallExpression(lhs.Loc.Union(loc), applicand, arguments); if (newStack.Count > 0) return PopNew(newStack, ce); else return ce; } case InputElementTag.LSquare: { var left = lhs; Consume(); var right = Expression(false); var loc = Only("index expression", "']'", InputElementTag.RSquare); return new IndexExpression(lhs.Loc.Union(loc), left, right); } case InputElementTag.Period: { var left = lhs; Consume(); if (Current.Tag != InputElementTag.Identifier) throw IEError("property access expression", "identifier"); var right = new StringLiteral(Current.Loc, Current.Value); Consume(); return new IndexExpression(lhs.Loc.Union(right.Loc), left, right); } default: return null; } }
// Collect and filter members for subsequent code gen private void CollectMembers() { var fields = new Seq<CST.FieldDef>(); var events = new Seq<CST.EventDef>(); var properties = new Seq<CST.PropertyDef>(); var methods = new Seq<CST.MethodDef>(); var exportedInstanceMethods = new Seq<CST.MethodDef>(); StaticInitializer = null; DefaultConstructor = null; numRelevantMethods = 0; foreach (var fieldDef in TyconEnv.Type.Members.OfType<CST.FieldDef>().Where(d => d.Invalid == null)) { if (fieldDef.IsUsed) fields.Add(fieldDef); else if (TypeTrace == null || TypeTrace.IncludeType) Env.Log (new UnusedDefinitionMessage (CST.MessageContextBuilders.Member (Env.Global, TyconEnv.Assembly, TyconEnv.Type, fieldDef))); } foreach (var eventDef in TyconEnv.Type.Members.OfType<CST.EventDef>().Where(d => d.Invalid == null)) { if (eventDef.IsUsed) events.Add(eventDef); else if (TypeTrace == null || TypeTrace.IncludeType) Env.Log (new UnusedDefinitionMessage (CST.MessageContextBuilders.Member (Env.Global, TyconEnv.Assembly, TyconEnv.Type, eventDef))); } foreach (var propDef in TyconEnv.Type.Members.OfType<CST.PropertyDef>().Where(d => d.Invalid == null)) { if (propDef.IsUsed) properties.Add(propDef); else if (TypeTrace == null || TypeTrace.IncludeType) Env.Log (new UnusedDefinitionMessage (CST.MessageContextBuilders.Member (Env.Global, TyconEnv.Assembly, TyconEnv.Type, propDef))); } var state = Env.InteropManager.GetTypeRepresentation(TyconEnv.Assembly, TyconEnv.Type).State; var s = TyconEnv.Type.Style; if (!(s is CST.InterfaceTypeStyle || s is CST.DelegateTypeStyle)) { foreach ( var methodDef in TyconEnv.Type.Members.OfType<CST.MethodDef>().Where(d => d.Invalid == null && !d.IsAbstract)) { if (!methodDef.IsUsed) { if (TypeTrace == null || TypeTrace.IncludeType) Env.Log (new UnusedDefinitionMessage (CST.MessageContextBuilders.Member (Env.Global, TyconEnv.Assembly, TyconEnv.Type, methodDef))); } else if (!Env.Validity.IsMustHaveADefinition(methodDef.QualifiedMemberName(Env.Global, TyconEnv.Assembly, TyconEnv.Type)) && (Env.InteropManager.IsInlinable(TyconEnv.Assembly, TyconEnv.Type, methodDef, state) || Env.InlinedMethods.IsInlinable(TyconEnv.Assembly, TyconEnv.Type, methodDef))) { if (TypeTrace == null || TypeTrace.IncludeType) Env.Log (new InlinedDefinitionMessage (CST.MessageContextBuilders.Member (Env.Global, TyconEnv.Assembly, TyconEnv.Type, methodDef))); } else if (state != InstanceState.JavaScriptOnly && state != InstanceState.ManagedAndJavaScript && !methodDef.IsStatic && methodDef.IsConstructor && methodDef.Arity > 1 && methodDef.ValueParameters[1].Equals(Env.JSContextRef)) { // Silently ignore importing constructors unless they are needed. // (Remember, the managed interop rewriter will interpert 'Merged' as // 'JavaScriptOnly', and thus may need importing constructors.) } else { if (methodDef.IsStatic && methodDef.IsConstructor) { if (methodDef.TypeArity > 0) throw new InvalidOperationException ("static constructors cannot be polymorphic"); StaticInitializer = new CST.MethodRef(TyconEnv.AddSelfTypeBoundArguments().TypeRef, methodDef.MethodSignature, null); } else if (!methodDef.IsStatic && methodDef.IsConstructor && methodDef.Arity == 1 && !Env.InteropManager.IsFactory(TyconEnv.Assembly, TyconEnv.Type, methodDef)) { if (methodDef.TypeArity > 0) throw new InvalidOperationException ("instance constructors cannot be polymorphic"); DefaultConstructor = new CST.MethodRef(TyconEnv.AddSelfTypeBoundArguments().TypeRef, methodDef.MethodSignature, null); } if (Env.InteropManager.IsExported(TyconEnv.Assembly, TyconEnv.Type, methodDef)) { if (Env.InteropManager.IsBindToInstance (TyconEnv.Assembly, TyconEnv.Type, methodDef)) exportedInstanceMethods.Add(methodDef); // else: will be exported by assembly's Initialize function } methods.Add(methodDef); if (TypeTrace == null || TypeTrace.Methods.Contains(methodDef.MethodSignature)) numRelevantMethods++; } } } // else: ignore members of interface and delegate types // TODO: Need to emit reflection data for methods of interface types, thus will need // to collect interface methods Fields = fields; Events = events; Properties = properties; Methods = methods; ExportedInstanceMethods = exportedInstanceMethods; }
public static MethodCompilerEnvironment EnterMethod (CompilerEnvironment env, JST.NameSupply outerNameSupply, JST.NameSupply nameSupply, JST.Identifier rootId, JST.Identifier assemblyId, JST.Identifier typeDefinitonId, CST.CompilationEnvironment compEnv, TypeTrace typeTrace) { // BUG: IE messes up scoping for function identifiers. To compensate we must allocate its // identifier in the outer scope var methodId = outerNameSupply.GenSym(); if (env.DebugMode) { var sb = new StringBuilder(); sb.Append(methodId.Value); sb.Append('_'); var namedTypeDef = compEnv.Type as CST.NamedTypeDef; if (namedTypeDef != null) { if (namedTypeDef.Name.Namespace.Length > 0) { JST.Lexemes.AppendStringToIdentifier(sb, namedTypeDef.Name.Namespace.Replace('.', '_')); sb.Append('_'); } foreach (var n in namedTypeDef.Name.Types) { JST.Lexemes.AppendStringToIdentifier(sb, n); sb.Append('_'); } } JST.Lexemes.AppendStringToIdentifier(sb, compEnv.Method.Name); methodId = new JST.Identifier(sb.ToString()); } var typeBoundTypeParameterIds = new Seq <JST.Identifier>(); for (var i = 0; i < compEnv.Type.Arity; i++) { typeBoundTypeParameterIds.Add(nameSupply.GenSym()); } var methodBoundTypeParameterIds = new Seq <JST.Identifier>(); for (var i = 0; i < compEnv.Method.TypeArity; i++) { methodBoundTypeParameterIds.Add(nameSupply.GenSym()); } var res = new MethodCompilerEnvironment (compEnv.Global, compEnv.SkolemDefs, compEnv.Assembly, compEnv.Type, compEnv.TypeBoundArguments, compEnv.Method, compEnv.MethodBoundArguments, compEnv.Variables, compEnv.ValueParameterIds, compEnv.LocalIds, env, nameSupply, rootId, assemblyId, typeDefinitonId, methodId, typeBoundTypeParameterIds, methodBoundTypeParameterIds, typeTrace); res.BindSpecial(); return(res); }