// ---------------------------------------------------------------------- // SPECIAL CASE: Imported methods // ---------------------------------------------------------------------- public JST.FunctionExpression ImportedMethod(CST.CSTWriter trace) { if (!env.InteropManager.IsImported(methEnv.Assembly, methEnv.Type, methEnv.Method)) return null; var isFactory = env.InteropManager.IsFactory(methEnv.Assembly, methEnv.Type, methEnv.Method); var delta = isFactory ? 1 : 0; var methCompEnv = MethodCompilerEnvironment.EnterUntranslatedMethod (env, outerNameSupply, nameSupply, rootId, assemblyId, typeDefinitionId, methEnv, parent.TypeTrace); var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); foreach (var id in methCompEnv.TypeBoundTypeParameterIds) parameters.Add(id); foreach (var id in methCompEnv.MethodBoundTypeParameterIds) parameters.Add(id); var valueParameters = new Seq<JST.Identifier>(); for (var i = delta; i < methCompEnv.Method.Arity; i++) { var id = methCompEnv.ValueParameterIds[i]; if (i == 0 && !methCompEnv.Method.IsStatic && methCompEnv.Type.Arity == 0) body.Add(JST.Statement.Var(id, new JST.ThisExpression())); else parameters.Add(id); valueParameters.Add(id); } // Take account of imports and exports on args/result to improve type sharing var usage = new CST.Usage(); for (var i = delta; i < methCompEnv.Method.Arity; i++) { if (!env.InteropManager.IsNoInteropParameter(methEnv.Assembly, methEnv.Type, methEnv.Method, i)) methCompEnv.SubstituteType(methCompEnv.Method.ValueParameters[i].Type).AccumUsage(usage, true); } if (isFactory) { if (!env.InteropManager.IsNoInteropParameter(methEnv.Assembly, methEnv.Type, methEnv.Method, 0)) methCompEnv.TypeRef.AccumUsage(usage, true); } else if (methCompEnv.Method.Result != null) { if (!env.InteropManager.IsNoInteropResult(methEnv.Assembly, methEnv.Type, methEnv.Method)) methCompEnv.SubstituteType(methCompEnv.Method.Result.Type).AccumUsage(usage, true); } methCompEnv.BindUsage(body, usage); if (methCompEnv.Method.IsConstructor && !methCompEnv.Method.IsStatic) { // Constructor or factory var isValType = methCompEnv.Type.Style is CST.ValueTypeStyle; var callArgs = new Seq<JST.Expression>(); var managedObjId = default(JST.Identifier); for (var i = delta; i < methCompEnv.Method.Arity; i++) { if (i == 0) // First argument is always the managed object or a managed pointer to value managedObjId = valueParameters[i]; else if (env.InteropManager.IsNoInteropParameter(methEnv.Assembly, methEnv.Type, methEnv.Method, i)) // Supress exports callArgs.Add(valueParameters[i].ToE()); else callArgs.Add (env.JSTHelpers.ExportExpressionForType (methCompEnv, methCompEnv.Method.ValueParameters[i].Type, valueParameters[i - delta].ToE())); } var call = env.InteropManager.AppendImport (nameSupply, rootId, methEnv.Assembly, methEnv.Type, methEnv.Method, body, callArgs); if (isValType) { if (isFactory) body.Add(new JST.ReturnStatement(call)); else body.Add(JST.Statement.DotCall(managedObjId.ToE(), Constants.PointerWrite, call)); } else { var state = env.InteropManager.GetTypeRepresentation(methEnv.Assembly, methEnv.Type).State; switch (state) { case InstanceState.ManagedOnly: if (isFactory) body.Add(new JST.ReturnStatement(call)); else throw new InvalidOperationException ("imported constructors of 'ManagedOnly' types must be factories"); break; case InstanceState.Merged: if (isFactory) { if (env.InteropManager.IsNoInteropParameter (methEnv.Assembly, methEnv.Type, methEnv.Method, 0)) body.Add(new JST.ReturnStatement(call)); else body.Add (new JST.ReturnStatement (env.JSTHelpers.ImportExpressionForType(methCompEnv, methCompEnv.TypeRef, call))); } else throw new InvalidOperationException ("imported constructors of 'ManagedOnly' types must be factories"); break; case InstanceState.ManagedAndJavaScript: case InstanceState.JavaScriptOnly: { var unmanagedObjId = nameSupply.GenSym(); body.Add(JST.Statement.Var(unmanagedObjId, call)); body.Add (JST.Statement.DotAssignment (managedObjId.ToE(), Constants.ObjectUnmanaged, unmanagedObjId.ToE())); body.Add (JST.Statement.DotCall (rootId.ToE(), state == InstanceState.ManagedAndJavaScript ? Constants.RootSetupManagedAndJavaScript : Constants.RootSetupJavaScriptOnly, managedObjId.ToE())); env.JSTHelpers.AppendInvokeImportingConstructor (methCompEnv, nameSupply, valueParameters, body, unmanagedObjId); break; } default: throw new ArgumentOutOfRangeException(); } } } else { if (isFactory) throw new InvalidOperationException("only constructors can be factories"); var outer = methCompEnv.Type.OuterPropertyOrEvent(methCompEnv.Method.MethodSignature); if (outer != null && outer.Flavor == CST.MemberDefFlavor.Event) { // Event adder/remover var eventDef = (CST.EventDef)outer; var simulateMulticast = env.InteropManager.IsSimulateMulticastEvents (methEnv.Assembly, methEnv.Type, eventDef); // Since event is in same type as this method, ok to use handler type directly var handlerType = eventDef.HandlerType; var slotName = new JST.StringLiteral (Constants.ObjectEventSlot(env.GlobalMapping.ResolveEventDefToSlot(methCompEnv.Assembly, methCompEnv.Type, eventDef))); var obj = default(JST.Expression); var delegateArg = default(JST.Expression); if (methCompEnv.Method.IsStatic) { obj = methCompEnv.ResolveType(methCompEnv.TypeRef); delegateArg = valueParameters[0].ToE(); } else { obj = valueParameters[0].ToE(); delegateArg = valueParameters[1].ToE(); } var slotExp = new JST.IndexExpression(obj, slotName); var callArgs = new Seq<JST.Expression>(); if (!methCompEnv.Method.IsStatic) { if (env.InteropManager.IsNoInteropParameter (methEnv.Assembly, methEnv.Type, methEnv.Method, callArgs.Count)) callArgs.Add(obj); else callArgs.Add (env.JSTHelpers.ExportExpressionForType (methCompEnv, methCompEnv.Method.ValueParameters[0].Type, obj)); } if (methCompEnv.Method.Signature.Equals(eventDef.Add)) { if (simulateMulticast) { body.Add (JST.Statement.DotCall (rootId.ToE(), Constants.RootAddEventHandler, obj, slotName, delegateArg)); if (env.InteropManager.IsNoInteropParameter (methEnv.Assembly, methEnv.Type, methEnv.Method, callArgs.Count)) callArgs.Add(slotExp); else callArgs.Add (env.JSTHelpers.ExportExpressionForType(methCompEnv, handlerType, slotExp)); } else callArgs.Add(delegateArg); body.Add (new JST.ExpressionStatement (env.InteropManager.AppendImport (nameSupply, rootId, methEnv.Assembly, methEnv.Type, methEnv.Method, body, callArgs))); } else if (methCompEnv.Method.Signature.Equals(eventDef.Remove)) { if (simulateMulticast) { body.Add (JST.Statement.DotCall (rootId.ToE(), Constants.RootRemoveEventHandler, obj, slotName, delegateArg)); if (env.InteropManager.IsNoInteropParameter (methEnv.Assembly, methEnv.Type, methEnv.Method, callArgs.Count)) callArgs.Add(slotExp); else callArgs.Add (env.JSTHelpers.ExportExpressionForType(methCompEnv, handlerType, slotExp)); } else callArgs.Add(new JST.NullExpression()); body.Add (new JST.ExpressionStatement (env.InteropManager.AppendImport (nameSupply, rootId, methEnv.Assembly, methEnv.Type, methEnv.Method, body, callArgs))); } else throw new InvalidOperationException("method not adder or remover"); } else { // Property getter/setter and normal methods var callArgs = new Seq<JST.Expression>(); for (var i = 0; i < methCompEnv.Method.Arity; i++) { if (env.InteropManager.IsNoInteropParameter(methEnv.Assembly, methEnv.Type, methEnv.Method, i)) callArgs.Add(valueParameters[i].ToE()); else callArgs.Add (env.JSTHelpers.ExportExpressionForType (methCompEnv, methEnv.Method.ValueParameters[i].Type, valueParameters[i].ToE())); } var call = env.InteropManager.AppendImport (nameSupply, rootId, methEnv.Assembly, methEnv.Type, methEnv.Method, body, callArgs); if (methCompEnv.Method.Result == null) body.Add(new JST.ExpressionStatement(call)); else if (env.InteropManager.IsNoInteropResult(methEnv.Assembly, methEnv.Type, methEnv.Method)) body.Add(new JST.ReturnStatement(call)); else body.Add (new JST.ReturnStatement (env.JSTHelpers.ImportExpressionForType (methCompEnv, methCompEnv.Method.Result.Type, call))); } } var func = default(JST.FunctionExpression); if (env.CLRInteropExceptions) { var exId = nameSupply.GenSym(); var funcBody = new Seq<JST.Statement>(); #if !JSCRIPT_IS_CORRECT funcBody.Add(JST.Statement.Var(exId)); #endif funcBody.Add (new JST.TryStatement (new JST.Statements(body), new JST.CatchClause (exId, new JST.Statements (new JST.ThrowStatement (JST.Expression.DotCall (rootId.ToE(), Constants.RootImportException, exId.ToE())))))); func = new JST.FunctionExpression(methCompEnv.MethodId, parameters, new JST.Statements(funcBody)); } else func = new JST.FunctionExpression(methCompEnv.MethodId, parameters, new JST.Statements(body)); if (trace != null) trace.Trace ("Imported JavaScript function", w => { func.Append(w); w.EndLine(); }); return func; }
private JST.FunctionExpression MethodImpl(CST.CSTWriter trace) { if (trace != null) trace.Trace ("Original IL method", w => { methEnv.Method.AppendDefinition(w); w.EndLine(); }); var func = ImportedMethod(trace) ?? NormalMethod(trace); // Simplify var simpCtxt = new JST.SimplifierContext(false, env.DebugMode, simpNameSupply, IsValue); func = (JST.FunctionExpression)func.Simplify(simpCtxt, EvalTimes.Bottom); if (trace != null) trace.Trace ("After JavaScript simplification", w => { func.Append(w); w.EndLine(); }); if (env.DebugMode) { // Add debugging assistance var l = 0; Func<int> nextLine = () => { return l++; }; var lineCountIds = new Set<JST.Identifier>(); var debugStmnts = WithLineCounts(func.Body, nextLine, 0, lineCountIds); lineCountIds.Add(Constants.DebugCurrentLine); var debugBody = new Seq<JST.Statement>(); debugBody.Add (new JST.VariableStatement(lineCountIds.Select(id => new JST.VariableDeclaration(id)).ToSeq())); foreach (var s in debugStmnts.Body) debugBody.Add(s); var exId = simpNameSupply.GenSym(); var funcBody = new Seq<JST.Statement>(); #if !JSCRIPT_IS_CORRECT funcBody.Add(JST.Statement.Var(exId)); #endif funcBody.Add (new JST.TryStatement (new JST.Statements(debugBody), new JST.CatchClause (exId, new JST.Statements (JST.Statement.DotCall(rootId.ToE(), Constants.RootDebugger, exId.ToE()), new JST.ThrowStatement(exId.ToE()))))); func = new JST.FunctionExpression(func.Name, func.Parameters, new JST.Statements(funcBody)); } if (trace != null) trace.Trace ("Final JavaScript method", w => { func.Append(w); w.EndLine(); }); return func; }
private JST.FunctionExpression NormalMethod(CST.CSTWriter trace) { var cstmethod = CST.CSTMethod.Translate(methEnv, nameSupply, trace); var methCompEnv = MethodCompilerEnvironment.EnterMethod (env, outerNameSupply, nameSupply, rootId, assemblyId, typeDefinitionId, cstmethod.CompEnv, parent.TypeTrace); var simpCtxt = new CST.SimplifierContext(methCompEnv, nameSupply, this, trace); cstmethod = cstmethod.Simplify(simpCtxt); if (trace != null) trace.Trace("After simplification of intermediate representation", w2 => cstmethod.Append(w2)); var usage = cstmethod.Body.Usage(methCompEnv); // Bind all type and value parameters var parameters = new Seq<JST.Identifier>(); var body = new Seq<JST.Statement>(); foreach (var id in methCompEnv.TypeBoundTypeParameterIds) parameters.Add(id); foreach (var id in methCompEnv.MethodBoundTypeParameterIds) parameters.Add(id); var delta = env.InteropManager.IsFactory(methCompEnv.Assembly, methCompEnv.Type, methCompEnv.Method) ? 1 : 0; for (var i = delta; i < methCompEnv.Method.Arity; i++) { var id = methCompEnv.ValueParameterIds[i]; if (i == 0 && !methCompEnv.Method.IsStatic && methCompEnv.Type.Arity == 0) { // Only instance methods of first-kinded types use 'this' for their first argument if (usage.Variables.ContainsKey(id)) body.Add(JST.Statement.Var(id, new JST.ThisExpression())); } else parameters.Add(id); } // Introduce the top level bindings based on usage methCompEnv.BindUsage(body, usage); // Introduce shared assembly/type/pointer bindings based on usage if (env.DebugMode) body.Add(new JST.CommentStatement("Locals")); var uninit = new Seq<JST.Identifier>(); foreach (var kv in usage.Variables) { var v = methCompEnv.Variable(kv.Key); if (v.ArgLocal == CST.ArgLocal.Local) { if (methCompEnv.Method.IsInitLocals && v.IsInit) body.Add (JST.Statement.Var(v.Id, env.JSTHelpers.DefaultExpressionForType(methCompEnv, v.Type))); else uninit.Add(v.Id); } } if (uninit.Count > 0) body.Add(new JST.VariableStatement(uninit.Select(id => new JST.VariableDeclaration(id)).ToSeq())); // Translate body to JavaScript statements/expressions foreach (var s in cstmethod.Body.Body) TranslateStatement(methCompEnv, body, s); var func = new JST.FunctionExpression(methCompEnv.MethodId, parameters, new JST.Statements(body)); if (trace != null) trace.Trace ("After translation to JavaScript", w => { func.Append(w); w.EndLine(); }); return func; }