public bool IsCacheable (JSMethod method) { if (method.Reference == null) return false; var type = method.Reference.DeclaringType; // Same-type calls are excluded if (TypeUtil.TypesAreEqual(type, ThisType)) return false; // Exclude any type that isn't in our bases or interfaces if (!TypeUtil.TypesAreAssignable(TypeInfo, type, ThisType)) return false; // TODO: Exclude interfaces? // Exclude generics if (TypeUtil.ContainsGenericParameter(type)) return false; if (TypeUtil.IsOpenType(type)) return false; return true; }
public FunctionAnalysis2ndPass GetSecondPass(JSMethod method) { var id = method.QualifiedIdentifier; Entry entry; if (!Cache.TryGetValue(id, out entry)) { entry = new Entry { Info = method.Method, Reference = method.Reference, Identifier = id, ParameterNames = new HashSet<string>(from p in method.Method.Parameters select p.Name), SecondPass = new FunctionAnalysis2ndPass(this, method.Method) }; Cache.Add(id, entry); OptimizationQueue.Add(id); return entry.SecondPass; } if (entry.SecondPass == null) { if (entry.InProgress) return null; if (entry.Expression == null) return null; else entry.SecondPass = new FunctionAnalysis2ndPass(this, GetFirstPass(id)); } return entry.SecondPass; }
public JSVerbatimLiteral(JSMethod originalMethod, string expression, IDictionary <string, JSExpression> variables, TypeReference type = null) : base(GetValues(variables)) { OriginalMethod = originalMethod; Type = type; Expression = expression; Variables = variables; }
public void VisitNode (JSMethod method) { var cm = GetCachedMethod(method); if (cm != null) { ParentNode.ReplaceChild(method, cm); VisitReplacement(cm); } else { VisitChildren(method); } }
private JSCachedMethod GetCachedMethod (JSMethod method) { if (!IsCacheable(method)) return null; var type = method.Reference.DeclaringType.Resolve(); if (type == null) return null; var identifier = new QualifiedMemberIdentifier( new TypeIdentifier(type), new MemberIdentifier(TypeInfo, method.Reference) ); CachedMethodRecord record; if (!CachedMethods.TryGetValue(identifier, out record)) CachedMethods.Add(identifier, record = new CachedMethodRecord(method, NextID++)); return new JSCachedMethod( method.Reference, method.Method, method.MethodTypes, method.GenericArguments, record.Index ); }
protected FunctionAnalysis2ndPass GetSecondPass(JSMethod method) { return FunctionSource.GetSecondPass(method, Member); }
protected JSExpression Translate_PropertyCall(JSExpression thisExpression, JSMethod method, JSExpression[] arguments, bool @virtual, bool @static) { var propertyInfo = method.Method.DeclaringProperty; if (propertyInfo == null) return null; if (propertyInfo.IsIgnored) return new JSIgnoredMemberReference(true, propertyInfo, arguments); // JS provides no way to override [], so keep it as a regular method call if (propertyInfo.Member.IsIndexer()) return null; var parms = method.Method.Metadata.GetAttributeParameters("JSIL.Meta.JSReplacement") ?? propertyInfo.Metadata.GetAttributeParameters("JSIL.Meta.JSReplacement"); if (parms != null) { var argsDict = new Dictionary<string, JSExpression>(); argsDict["this"] = thisExpression; argsDict["typeof(this)"] = new JSType(thisExpression.GetExpectedType(TypeSystem)); foreach (var kvp in method.Method.Parameters.Zip(arguments, (p, v) => new { p.Name, Value = v })) { argsDict.Add(kvp.Name, kvp.Value); } return new JSVerbatimLiteral(method, (string)parms[0].Value, argsDict, propertyInfo.ReturnType); } var thisType = GetTypeDefinition(thisExpression.GetExpectedType(TypeSystem)); Func<JSExpression> generate = () => { var actualThis = @static ? new JSType(method.Method.DeclaringType.Definition) : thisExpression; if ((method.Reference.DeclaringType is GenericInstanceType) && !method.Reference.HasThis) { actualThis = new JSType(method.Reference.DeclaringType); } if ((propertyInfo.Member.GetMethod != null) && (method.Method.Member.Name == propertyInfo.Member.GetMethod.Name)) { return new JSPropertyAccess( actualThis, new JSProperty(method.Reference, propertyInfo) ); } else { if (arguments.Length == 0) throw new InvalidOperationException("Attempting to invoke a property setter with no arguments"); return new JSBinaryOperatorExpression( JSOperator.Assignment, new JSPropertyAccess( actualThis, new JSProperty(method.Reference, propertyInfo) ), arguments[0], propertyInfo.ReturnType ); } }; // Accesses to a base property should go through a regular method invocation, since // javascript properties do not have a mechanism for base access if (method.Method.Member.HasThis) { if (!TypesAreEqual(method.Method.DeclaringType.Definition, thisType) && !@virtual) { return null; } else { return generate(); } } return generate(); }
protected JSExpression Translate_MethodReplacement( JSMethod method, JSExpression thisExpression, JSExpression[] arguments, bool @virtual, bool @static, bool explicitThis ) { var methodInfo = method.Method; var metadata = methodInfo.Metadata; if (metadata != null) { var parms = metadata.GetAttributeParameters("JSIL.Meta.JSReplacement"); if (parms != null) { var argsDict = new Dictionary<string, JSExpression>(); argsDict["this"] = thisExpression; argsDict["typeof(this)"] = new JSType(thisExpression.GetExpectedType(TypeSystem)); foreach (var kvp in methodInfo.Parameters.Zip(arguments, (p, v) => new { p.Name, Value = v })) { argsDict.Add(kvp.Name, kvp.Value); } return new JSVerbatimLiteral( method, (string)parms[0].Value, argsDict, method.Method.ReturnType ); } } if (methodInfo.IsIgnored) return new JSIgnoredMemberReference(true, methodInfo, new[] { thisExpression }.Concat(arguments).ToArray()); switch (method.Method.Member.FullName) { case "System.Object JSIL.Builtins::Eval(System.String)": return JSInvocationExpression.InvokeStatic( JS.eval, arguments ); case "System.Object JSIL.Verbatim::Expression(System.String)": { var expression = arguments[0] as JSStringLiteral; if (expression == null) throw new InvalidOperationException("JSIL.Verbatim.Expression must recieve a string literal as an argument"); return new JSVerbatimLiteral( method, expression.Value, null, null ); } case "System.Object JSIL.JSGlobal::get_Item(System.String)": { var expression = arguments[0] as JSStringLiteral; if (expression != null) return new JSDotExpression( JSIL.GlobalNamespace, new JSStringIdentifier(expression.Value, TypeSystem.Object) ); else return new JSIndexerExpression( JSIL.GlobalNamespace, arguments[0], TypeSystem.Object ); } case "System.Object JSIL.JSLocal::get_Item(System.String)": { var expression = arguments[0] as JSStringLiteral; if (expression == null) throw new InvalidOperationException("JSLocal must recieve a string literal as an index"); return new JSStringIdentifier(expression.Value, TypeSystem.Object); } case "System.Object JSIL.Builtins::get_This()": return new JSIndirectVariable(Variables, "this", ThisMethodReference); } JSExpression result = Translate_PropertyCall(thisExpression, method, arguments, @virtual, @static); if (result == null) { if (@static) result = JSInvocationExpression.InvokeStatic(method.Reference.DeclaringType, method, arguments); else if (explicitThis) result = JSInvocationExpression.InvokeBaseMethod(method.Reference.DeclaringType, method, thisExpression, arguments); else result = JSInvocationExpression.InvokeMethod(method.Reference.DeclaringType, method, thisExpression, arguments); } return result; }
private JSExpression ConstructInvocation ( JSPropertyAccess pa, JSExpression argument = null ) { JSExpression[] arguments; if (argument == null) arguments = new JSExpression[0]; else arguments = new JSExpression[] { argument }; var originalMethod = pa.OriginalMethod; var declaringType = originalMethod.Reference.DeclaringType; var declaringTypeDef = TypeUtil.GetTypeDefinition(originalMethod.Reference.DeclaringType); var thisReferenceType = pa.ThisReference.GetActualType(TypeSystem); var isSelf = TypeUtil.TypesAreAssignable( TypeInfo, thisReferenceType, declaringType ); // ILSpy converts compound assignments from: // x.set_Value(x.get_Value + n) // to // x.get_Value += n; // so we have to detect this and reconstruct the correct method // references. var actualMethod = originalMethod; var correctName = String.Format( "{0}_{1}", pa.IsWrite ? "set" : "get", pa.Property.Property.ShortName ); if (!actualMethod.Reference.Name.Contains(correctName)) { var dt = originalMethod.Method.DeclaringType; var actualMethodInfo = dt.Members.Values .OfType<MethodInfo>().FirstOrDefault( (m) => ( m.Name.Contains(correctName) && m.DeclaringType == originalMethod.Method.DeclaringType ) ); MethodReference actualMethodReference = actualMethodInfo.Member; if (originalMethod.Reference is GenericInstanceMethod) { throw new InvalidDataException("Reconstructing an invocation of a generic instance method? Shouldn't be possible."); } else if (declaringType is GenericInstanceType) { var declaringGit = (GenericInstanceType)declaringType; var returnType = actualMethodReference.ReturnType; if (TypeUtil.IsOpenType(returnType)) { var actualReturnType = JSExpression.SubstituteTypeArgs(TypeInfo, actualMethodReference.ReturnType, actualMethodReference); returnType = actualReturnType; } actualMethodReference = new MethodReference( actualMethodReference.Name, returnType, declaringGit ); } actualMethod = new JSMethod( actualMethodReference, actualMethodInfo, originalMethod.MethodTypes, originalMethod.GenericArguments ); } bool needsExplicitThis = !pa.IsVirtualCall && ILBlockTranslator.NeedsExplicitThis( declaringType, declaringTypeDef, originalMethod.Method.DeclaringType, isSelf, thisReferenceType, originalMethod.Method ); JSInvocationExpressionBase invocation; if (pa.Property.Property.IsStatic) invocation = JSInvocationExpression.InvokeStatic( actualMethod.Reference.DeclaringType, actualMethod, arguments ); else if (needsExplicitThis) invocation = JSInvocationExpression.InvokeBaseMethod( actualMethod.Reference.DeclaringType, actualMethod, pa.ThisReference, arguments ); else invocation = JSInvocationExpression.InvokeMethod( pa.OriginalType, actualMethod, pa.ThisReference, arguments ); JSExpression replacement; if (TypeUtil.IsStruct(pa.Property.Property.ReturnType)) replacement = new JSResultReferenceExpression(invocation); else replacement = invocation; return replacement; }
public CachedMethodRecord (JSMethod method, int index) { Method = method; Index = index; }
public FunctionAnalysis2ndPass GetSecondPass (JSMethod method, QualifiedMemberIdentifier forCaller) { if (method == null) return null; var id = method.QualifiedIdentifier; Entry entry = Cache.GetOrCreate( id, method, MakeCacheEntry ); if (entry == null) return null; GetFirstPass(id, forCaller); if (!TryAcquireStaticAnalysisDataLock(entry, method.QualifiedIdentifier)) return null; try { return _GetOrCreateSecondPass(entry); } finally { entry.StaticAnalysisDataLock.Exit(); } }
public FunctionAnalysis2ndPass GetSecondPass(JSMethod method) { var id = method.QualifiedIdentifier; Entry entry = Cache.GetOrCreate( id, method, MakeCacheEntry ); if (entry.SecondPass == null) { if (entry.InProgress) return null; if (entry.Expression == null) return null; else { var firstPass = GetFirstPass(id); try { entry.InProgress = true; entry.SecondPass = new FunctionAnalysis2ndPass(this, firstPass); } finally { entry.InProgress = false; } } } return entry.SecondPass; }
private void EmitCctors() { // HACK var identifier = (MemberIdentifier)Activator.CreateInstance( typeof(MemberIdentifier), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic, null, new object[] { true, MemberIdentifier.MemberType.Method, ".cctor", EntryPointAstEmitter.TypeSystem.Void, null, 0 }, null ); // Find types we emitted that have static constructors var cctors = ( from t in TypesToStaticInitialize let ti = Translator.TypeInfoProvider.GetExisting(t) where ti.Members.ContainsKey(identifier) let mi = ti.Members[identifier] select (MethodInfo)mi ).ToList(); if (cctors.Count == 0) return; NeedStaticInit = true; Formatter.NewLine(); Formatter.WriteRaw(";; Compiler-generated static constructor dispatcher"); Formatter.NewLine(); // If we found any, we need to generate a special function that invokes all the cctors Formatter.WriteRaw("(func $__static_init (block "); Formatter.Indent(); Formatter.NewLine(); // FIXME: Walk cctor dependencies and invoke in correct order foreach (var cctor in cctors) { // Synthesize a regular static method call var jsm = new JSMethod( cctor.Member, cctor, Translator.FunctionCache.MethodTypes ); var call = JSInvocationExpression.InvokeStatic(jsm, new JSExpression[0], false); // HACK EntryPointAstEmitter.Emit(call); Formatter.ConditionalNewLine(); } Formatter.Unindent(); Formatter.ConditionalNewLine(); Formatter.WriteRaw(") )"); Formatter.NewLine(); Formatter.NewLine(); Formatter.WriteRaw("(export \"__static_init\" $__static_init)"); Formatter.NewLine(); }
public JSInvocationExpression NewDelegate (TypeReference delegateType, JSExpression thisReference, JSExpression targetMethod, JSMethod method) { return JSInvocationExpression.InvokeStatic( new JSDotExpression( new JSType(delegateType), new JSFakeMethod("New", delegateType, new[] { TypeSystem.Object, TypeSystem.Object }, MethodTypes) ), method == null ? new [] { thisReference, targetMethod } : new [] { thisReference, targetMethod, new JSDefferedExpression(new JSMethodOfExpression(method.Reference, method.Method, method.MethodTypes, method.GenericArguments)), }, true ); }
protected FunctionAnalysis2ndPass GetSecondPass(JSMethod method) { return(FunctionSource.GetSecondPass(method, Member)); }
public FunctionAnalysis2ndPass GetSecondPass(JSMethod method) { var id = method.QualifiedIdentifier; Entry entry = Cache.GetOrCreate( id, () => { OptimizationQueue.TryEnqueue(id); return new Entry { Info = method.Method, Reference = method.Reference, Identifier = id, ParameterNames = new HashSet<string>(from p in method.Method.Parameters select p.Name), SecondPass = new FunctionAnalysis2ndPass(this, method.Method) }; } ); if (entry.SecondPass == null) { if (entry.InProgress) return null; if (entry.Expression == null) return null; else { var firstPass = GetFirstPass(id); try { entry.InProgress = true; entry.SecondPass = new FunctionAnalysis2ndPass(this, firstPass); } finally { entry.InProgress = false; } } } return entry.SecondPass; }