protected override object VisitStoreField(Variable dest, Field field, Variable source, Statement stat, object arg) { ExposureState estate = (ExposureState)arg; // static Fields if (field.IsStatic) { } // BUGBUG!! // It seems that it would be better to start off ctors with the method's "this" object // in the Exposed state, but I'm not sure how to do that. This t = null; ThisBinding tb = dest as ThisBinding; if (tb != null) { t = tb.BoundThis; } else { t = dest as This; } if (t != null && this.ExposureChecker.currentMethod.NodeType == NodeType.InstanceInitializer && this.ExposureChecker.currentMethod.ThisParameter == t) { ; // skip } else { ExposureState.Lattice.AVal valueOfdest = estate.GetAVal(dest); if (valueOfdest.lowerBound == null || valueOfdest.upperBound == null) { HandleError(stat, stat, Error.WritingPackedObject, dest.Name.Name); return(arg); } if (valueOfdest.lowerBound.IsAssignableTo(field.DeclaringType)) { HandleError(stat, stat, Error.WritingPackedObject, dest.Name.Name); return(arg); } } return(arg); }
private void ProcessMethod(StatementList setupInteropStatements, Set<DelegateNode> accumDelegateTypes, Method method) { try { if (env.InteropManager.IsImported(null, method)) { AddDelegateTypes(accumDelegateTypes, method); // If constructor: // C(A1 a1, A2 a2) // emit: // var ci = typeof(<this type>).GetConstructor(new Type[] { typeof(A1), typeof(A2) }); // var ctxt = (JSContext)InteropContextManager.CurrentRuntime.CallImportedMethod( // ci, // <import script>, // new object[] { this, a1, a2 }); // C(ctxt, a1, a2) or C(ctxt); // InteropContextManager.CurrentRuntime.CompleteConstruction(ci, this, ctxt); // // If static method: // R C::M(A1 a1, A2 a2) // emit: // return (R)InteropContextManager.CurrentRuntime.CallImportedMethod( // typeof(<this type>).GetMethod("M", new Type[] { typeof(A1), typeof(A2) }), // <import script>, // new object[] { a1, a2 }); // // If instance method: // R C::M(A1 a1, A2 a2) // emit: // return (R)InteropContextManager.GetRuntimeForObject(this).CallImportedMethod( // typeof(<this type>).GetMethod("M", new Type[] { typeof(A1), typeof(A2) }), // <import script>, // new object[] { this, a1, a2 }); var thisExpr = new ThisBinding(ThisExpression(method.DeclaringType), method.SourceContext); var argExprs = new ExpressionList(); if (!method.IsStatic && !(method is InstanceInitializer)) argExprs.Add(thisExpr); foreach (var p in method.Parameters) argExprs.Add (BoxExpression(new ParameterBinding(p, method.SourceContext), env.ObjectType)); var argArray = ArrayExpression(argExprs, env.ObjectType); // Imports are special in a few ways: // - The runtime will never attempt to Invoke the method base. All it needs are the // argument types, static/instance distiction, and method/constructor distinction. // - The call to Runtime::CallImportedMethod will be within the method body // itself. If the method is polymorphic, and/or within a higher-kinded type, then // typeof(<argument type>) will yield the correct runtime type for the argument, taking // account of all type instantiation. We don't need to know the type arguments themselves. // - Private methods may be imported, however Silverlight doesn't provide reflection for // private methods. // For these reasons we build our own simple-minded method base literal to support the // CallImportedMethod call. var methodBaseExpr = SimpleMethodBaseExpression(method); var runtimeExpr = default(Expression); if (method.IsStatic || method is InstanceInitializer) runtimeExpr = new MethodCall (new MemberBinding(null, env.InteropContextManager_GetCurrentRuntimeMethod), new ExpressionList(0)); else runtimeExpr = new MethodCall (new MemberBinding(null, env.InteropContextManager_GetRuntimeForObjectMethod), new ExpressionList(thisExpr)); var si = env.InteropManager.ImportInfo(null, env.GenSym, new JST.Identifier(env.Root), method); var scriptString = si.Script.ToString(false); env.Log(new InteropInfoMessage(RewriterMsgContext.Method(method), "Imported as: " + scriptString)); var scriptExpr = new Literal(scriptString, env.StringType); var statements = method.Body.Statements; var ctor = method as InstanceInitializer; if (ctor != null) { var locals = ctor.LocalList; if (locals == null) { locals = new LocalList(2); ctor.LocalList = locals; } var constructorInfoLocal = new Local(Identifier.For("ci"), env.SimpleMethodBaseType); locals.Add(constructorInfoLocal); var contextLocal = new Local(Identifier.For("ctxt"), env.JSContextType); locals.Add(contextLocal); statements.Add (new AssignmentStatement (new LocalBinding(constructorInfoLocal, ctor.SourceContext), methodBaseExpr)); statements.Add (new AssignmentStatement (new LocalBinding(contextLocal, ctor.SourceContext), CastExpression (new MethodCall (new MemberBinding (runtimeExpr, env.Runtime_CallImportedMethodMethod), new ExpressionList (new LocalBinding(constructorInfoLocal, ctor.SourceContext), scriptExpr, argArray)), env.JSContextType))); var importingCtor = BestImportingConstructor(ctor); var args = new ExpressionList(importingCtor.Parameters.Count); args.Add(new LocalBinding(contextLocal, ctor.SourceContext)); if (importingCtor.Parameters.Count > 1) { for (var i = 0; i < ctor.Parameters.Count; i++) args.Add(new ParameterBinding(ctor.Parameters[i], ctor.SourceContext)); } statements.Add (new ExpressionStatement (new MethodCall(new MemberBinding(thisExpr, importingCtor), args))); statements.Add (new ExpressionStatement (new MethodCall (new MemberBinding(runtimeExpr, env.Runtime_CompleteConstructionMethod), new ExpressionList (new LocalBinding(constructorInfoLocal, ctor.SourceContext), thisExpr, new LocalBinding(contextLocal, ctor.SourceContext))))); statements.Add(new Return()); } else if (method.ReturnType == env.VoidType) { statements.Add (new ExpressionStatement (new MethodCall (new MemberBinding(runtimeExpr, env.Runtime_CallImportedMethodMethod), new ExpressionList(methodBaseExpr, scriptExpr, argArray)))); statements.Add(new ExpressionStatement(new UnaryExpression(null, NodeType.Pop))); statements.Add(new Return()); } else { statements.Add (new Return (CastExpression (new MethodCall (new MemberBinding (runtimeExpr, env.Runtime_CallImportedMethodMethod), new ExpressionList(methodBaseExpr, scriptExpr, argArray)), method.ReturnType))); } TagAsInteropGenerated(method); } if (env.InteropManager.IsExported(null, method)) { AddDelegateTypes(accumDelegateTypes, method); // For each exported method, append to <Module>::SetupInterop() // InteropContextManager.Database.RegisterExport(<method base of M>, <bind to instance>, <cature this>, <export script>); // Exports are special in a few ways: // - Polymorphic methods cannot be exported, so we never need to deal with them. // - The call to Runtime::RegisterExportMethod is outside of the method itself. For instance // methods, the declaring type may be higher-kinded, in which case we must recover the // type arguments from the type of the instance at runtime. Thus at compile-time we // must describe the method in it's higher-kinded declaring type. // - The runtime needs to be able to Invoke the method base. // Thus we are forced to use true MethodBase, MethodInfo and ConstructorInfo, and work-around // limitations of reflection. var si = env.InteropManager.ExportInfo(null, env.GenSym, new JST.Identifier(env.Root), method); var scriptString = si.Script.ToString(false); env.Log(new InteropInfoMessage(RewriterMsgContext.Method(method), "Exported as: " + scriptString)); setupInteropStatements.Add (new ExpressionStatement (new MethodCall (new MemberBinding (DatabaseExpression(), env.InteropDatabase_RegisterExportMethod), new ExpressionList (MethodBaseExpression(method), new Literal(si.BindToInstance, env.BooleanType), new Literal(scriptString, env.StringType))))); } } catch (DefinitionException) { env.Log(new InvalidInteropMessage(RewriterMsgContext.Method(method), "Method contains interop specification errors")); } }
private void ProcessType(StatementList setupInteropStatements, Set<DelegateNode> accumDelegateTypes, TypeNode type) { try { var style = env.InteropManager.Style(null, type); if (style == InteropStyle.Delegate) { var delType = (DelegateNode)type; var di = env.InteropManager.DelegateInfo(null, delType); env.Log(new InteropInfoMessage(RewriterMsgContext.Type(type), "Registering type as: " + style)); setupInteropStatements.Add (new ExpressionStatement (new MethodCall (new MemberBinding (DatabaseExpression(), env.InteropDatabase_RegisterTypeMethod), new ExpressionList (TypeOfExpression(type), new Literal((int)style, env.IntType), new Literal(null, env.StringType), new Literal(null, env.StringType), new Literal(0, env.IntType), new Literal(di.CaptureThis, env.BooleanType), new Literal(di.InlineParamsArray, env.BooleanType), new Literal(false, env.BooleanType))))); } else if (style == InteropStyle.Proxied || style == InteropStyle.Keyed) { // Append to <Module>::SetupInterop(): // InteropContextManager.Database.RegisterType( // <index of type>, // Keyed or Proxied, // <JavaScript fragment for key field, or null>, // <JavaScript type classifier function, or null>, // <steps to root type>, // false); var rootTypeSteps = env.InteropManager.RootTypeSteps(null, type); var undefinedIsNotNull = false; var keyFieldStr = default(string); var classifierStr = default(string); if (rootTypeSteps == 0) { if (style == InteropStyle.Keyed) keyFieldStr = env.InteropManager.KeyField(null, type).ToString(false); var classifierJS = env.InteropManager.TypeClassifier(null, type); classifierStr = classifierJS == null ? null : classifierJS.ToString(false); } if (style == InteropStyle.Proxied) undefinedIsNotNull = env.InteropManager.UndefinedIsNotNull(null, type); env.Log(new InteropInfoMessage(RewriterMsgContext.Type(type), "Registering type as: " + style.ToString())); setupInteropStatements.Add (new ExpressionStatement (new MethodCall (new MemberBinding (DatabaseExpression(), env.InteropDatabase_RegisterTypeMethod), new ExpressionList (TypeOfExpression(type), new Literal((int)style, env.IntType), new Literal(keyFieldStr, env.StringType), new Literal(classifierStr, env.StringType), new Literal(rootTypeSteps, env.IntType), new Literal(false, env.BooleanType), new Literal(false, env.BooleanType), new Literal(undefinedIsNotNull, env.BooleanType))))); // Create default importing constructor if none supplied by user // - If derive from base with default importing constructor, invoke that. // - If derive from 'Normal' base with default constructor, invoke that. // - Otherwise error var importingCtor = DefaultImportingConstructor(type); if (importingCtor == null) { var thisExpr = new ThisBinding(ThisExpression(type), type.SourceContext); var parameters = new ParameterList(1); parameters.Add(new Parameter(Identifier.For("ctxt"), env.JSContextType)); var statements = new StatementList(1); var baseType = type.BaseType; if (baseType == null) // Object is 'Normal', so this is never possible throw new InvalidOperationException("no base type"); var baseDefaultImportingCtor = DefaultImportingConstructor(baseType); if (baseDefaultImportingCtor != null) { env.Log(new InteropInfoMessage(RewriterMsgContext.Type(type), "Created default importing constructor chained from base type's default importing constructor")); statements.Add (new ExpressionStatement (new MethodCall (new MemberBinding(thisExpr, baseDefaultImportingCtor), new ExpressionList (new ParameterBinding(parameters[0], type.SourceContext))))); } else { if (env.InteropManager.Style(null, baseType) == InteropStyle.Normal) { var baseDefaultCtor = baseType.GetConstructor(); if (baseDefaultCtor == null) { env.Log(new InteropInfoMessage (RewriterMsgContext.Type(type), "Cannot create a default importing constructor for type, since it derives from a type with state 'ManagedOnly' which does not contain a default constructor")); throw new DefinitionException(); } env.Log(new InteropInfoMessage(RewriterMsgContext.Type(type), "Created default importing constructor chained from base type's default constructor")); statements.Add (new ExpressionStatement (new MethodCall (new MemberBinding(thisExpr, baseDefaultCtor), new ExpressionList(0)))); } else { var hkType = default(TypeNode); var classTypeArguments = default(Seq<TypeNode>); ExplodeTypeApplication(baseType, out hkType, out classTypeArguments); if (classTypeArguments != null && classTypeArguments.Count > 0) { env.Log(new InteropInfoMessage (RewriterMsgContext.Type(type), "Cannot create a default importing constructor for type, since it derives from an instance of a higher-kinded type without an explicit default importing constructor. (This limitation will be removed in the future.)")); } else { env.Log(new InteropInfoMessage (RewriterMsgContext.Type(type), "Cannot create a default importing constructor for type, since it derives from a type with state 'ManagedAndJavaScript' or 'JavaScriptOnly', and that type does not contain a default importing constructor")); } throw new DefinitionException(); } } statements.Add(new Return()); importingCtor = new InstanceInitializer (type, new AttributeList(0), parameters, new Block(statements)); importingCtor.Flags |= MethodFlags.Public; importingCtor.DeclaringType = type; TagAsCompilerGenerated(importingCtor); // il2jsc can compile this importing ctor as if it were written by the user, // so no need for any 'InteropGenerated' attribute. type.Members.Add(importingCtor); } } // Remember: a type containing only static imports/exports may appear 'Normal' if (style == InteropStyle.Normal || style == InteropStyle.Primitive || style == InteropStyle.Proxied || style == InteropStyle.Keyed) { foreach (var member in type.Members) { var nestedType = member as TypeNode; if (nestedType != null) ProcessType(setupInteropStatements, accumDelegateTypes, nestedType); else { var method = member as Method; if (method != null) ProcessMethod(setupInteropStatements, accumDelegateTypes, method); } } } } catch (DefinitionException) { env.Log(new InvalidInteropMessage(RewriterMsgContext.Type(type), "Type contains interop specification errors")); } }