// 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 }; }
// 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 }; }
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); } } }
// ---------------------------------------------------------------------- // Entry point from TypeCompiler // ---------------------------------------------------------------------- public void Emit(ISeq<JST.Statement> body, JST.Expression target) { if (env.BreakOnBreak && env.AttributeHelper.MethodHasAttribute(methEnv.Assembly, methEnv.Type, methEnv.Method, env.AttributeHelper.BreakAttributeRef, false, false)) System.Diagnostics.Debugger.Break(); var methodName = CST.CSTWriter.WithAppend(env.Global, CST.WriterStyle.Debug, methEnv.MethodRef.Append); var methodSlot = env.GlobalMapping.ResolveMethodDefToSlot(methEnv.Assembly, methEnv.Type, methEnv.Method); var method = Method(methodName); switch (mode) { case MethodCompilationMode.SelfContained: { if (target != null) throw new InvalidOperationException("not expecting target in self-contained mode"); var assmName = CST.CSTWriter.WithAppend (env.Global, CST.WriterStyle.Uniform, methEnv.Assembly.Name.Append); var typeSlot = env.GlobalMapping.ResolveTypeDefToSlot(methEnv.Assembly, methEnv.Type); var func = new JST.FunctionExpression (new Seq<JST.Identifier> { rootId, assemblyId, typeDefinitionId }, new JST.Statements(new JST.ReturnStatement(method))); var methodLoader = new Seq<JST.Statement>(); if (env.DebugMode) methodLoader.Add(new JST.CommentStatement(methodName)); methodLoader.Add (JST.Statement.DotCall (new JST.Identifier(env.Root).ToE(), Constants.RootBindMethod, new JST.StringLiteral(assmName), new JST.StringLiteral(typeSlot), new JST.BooleanLiteral(env.InteropManager.IsStatic(methEnv.Assembly, methEnv.Type, methEnv.Method)), new JST.StringLiteral(methodSlot), func)); var methodProgram = new JST.Program(new JST.Statements(methodLoader)); var methodFileName = Path.Combine (env.OutputDirectory, Path.Combine (JST.Lexemes.StringToFileName(assmName), Path.Combine(typeSlot, Path.Combine(methodSlot, Constants.MethodFileName)))); methodProgram.ToFile(methodFileName, env.PrettyPrint); env.Log(new GeneratedJavaScriptFile("method '" + methEnv.MethodRef + "'", methodFileName)); break; } case MethodCompilationMode.DirectBind: { if (target == null) throw new InvalidOperationException("expecting target in self-contained mode"); if (env.DebugMode) body.Add(new JST.CommentStatement(methodName)); body.Add (JST.Statement.Assignment(JST.Expression.Dot(target, new JST.Identifier(methodSlot)), method)); break; } default: throw new ArgumentOutOfRangeException(); } }
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; }
// ---------------------------------------------------------------------- // 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 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; }
private JST.Expression HandlerLiteral(MethodCompilerEnvironment methCompEnv, CST.TryPseudoStatementHandler handler) { switch (handler.Flavor) { case CST.HandlerFlavor.Catch: { var catchh = (CST.CatchTryPseudoStatementHandler)handler; var exid = nameSupply.GenSym(); var type = methCompEnv.ResolveType(catchh.ExceptionType); var match = JST.Expression.IsNotNull (JST.Expression.DotCall(rootId.ToE(), Constants.RootIsInst, exid.ToE(), type)); var pred = new JST.FunctionExpression (new Seq<JST.Identifier> { exid }, new JST.Statements (new JST.IfStatement (match, new JST.Statements (JST.Statement.Assignment(catchh.ExceptionId.ToE(), exid.ToE()), new JST.ReturnStatement(new JST.BooleanLiteral(true))), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false)))))); return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.HandlerStyle, new JST.NumericLiteral(0) }, { Constants.HandlerTarget, new JST.NumericLiteral(catchh.HandlerId) }, { Constants.HandlerPred, pred } }); } case CST.HandlerFlavor.Fault: return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.HandlerStyle, new JST.NumericLiteral(1) }, { Constants.HandlerTarget, new JST.NumericLiteral(handler.HandlerId) } }); case CST.HandlerFlavor.Finally: return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.HandlerStyle, new JST.NumericLiteral(2) }, { Constants.HandlerTarget, new JST.NumericLiteral(handler.HandlerId) } }); case CST.HandlerFlavor.Filter: throw new InvalidOperationException("filter blocks not supported"); default: throw new ArgumentOutOfRangeException(); } }