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 )); }
public Invocation( int[] parentNodeIndices, int statementIndex, int nodeIndex, JSVariable thisVariable, JSMethod method, object nonJSMethod, IDictionary <string, string[]> variables ) : base(parentNodeIndices, statementIndex, nodeIndex) { if (thisVariable != null) { ThisVariable = thisVariable.Identifier; } else { ThisVariable = null; } ThisType = null; Method = method; if (method == null) { NonJSMethod = nonJSMethod; } else { NonJSMethod = null; } Variables = variables; }
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); }
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 Invocation(int statementIndex, int nodeIndex, JSVariable thisVariable, JSMethod method, IDictionary <string, string[]> variables) : base(statementIndex, nodeIndex) { ThisVariable = thisVariable.Identifier; ThisType = null; Method = method; Variables = variables; }
/// <summary> /// Writes an interface member reference to the output. /// </summary> public void WriteInterfaceMemberToOutput( JavascriptFormatter output, Compiler.Extensibility.IAstEmitter astEmitter, JSFunctionExpression enclosingFunction, JSMethod jsMethod, JSExpression method, TypeReferenceContext referenceContext ) { int index; CachedInterfaceMemberRecord record; GenericParameter[] rewrittenGenericParameters = null; if (LocalCachingEnabled && PreferLocalCacheForGenericInterfaceMethodSignatures) { record = new CachedInterfaceMemberRecord(jsMethod.Reference.DeclaringType, jsMethod.Identifier); } else { var rewritten = GenericTypesRewriter.Normalized(jsMethod.Reference.DeclaringType); record = new CachedInterfaceMemberRecord(rewritten.CacheRecord, jsMethod.Identifier, rewritten.RewritedGenericParameters.Length); rewrittenGenericParameters = rewritten.RewritedGenericParameters; } if (enclosingFunction.Method != null && enclosingFunction.Method.Method != null) { var functionIdentifier = enclosingFunction.Method.Method.Identifier; CacheSet localSignatureSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSignatureSet)) { if (localSignatureSet.InterfaceMembers.TryGetValue(record, out index)) { output.WriteRaw("$im{0:X2}", index); return; } } } if (!Global.InterfaceMembers.TryGetValue(record, out index)) { output.Identifier(jsMethod.Reference.DeclaringType, referenceContext, false); output.Dot(); astEmitter.Emit(method); } else { output.WriteRaw("$IM{0:X2}", index); output.LPar(); if (rewrittenGenericParameters != null) { output.CommaSeparatedList(rewrittenGenericParameters, referenceContext); } output.RPar(); } }
private static string GetMethodName(JSMethod method) { if (method == null) { return("?"); } else { return(method.Reference.Name); } }
public Invocation( int[] parentNodeIndices, int statementIndex, int nodeIndex, JSType type, JSMethod method, IDictionary <string, string[]> variables ) : base(parentNodeIndices, statementIndex, nodeIndex) { ThisType = type; ThisVariable = null; Method = method; Variables = variables; }
public void VisitNode(JSMethod method) { Output.Identifier(method.GetNameForInstanceReference()); var ga = method.GenericArguments; if (ga != null) { Output.LPar(); Output.CommaSeparatedList(ga, ReferenceContext, ListValueType.Identifier); Output.RPar(); } }
public void VisitNode(JSMethod method) { var cm = GetCachedMethod(method); if (cm != null) { ParentNode.ReplaceChild(method, cm); VisitReplacement(cm); } else { VisitChildren(method); } }
public void VisitNode(JSNewExpression newexp) { if ((newexp.ConstructorReference != null) && (newexp.Constructor != null)) { var jsm = new JSMethod(newexp.ConstructorReference, newexp.Constructor, FunctionSource.MethodTypes); var variables = ExtractAffectedVariables(jsm, newexp.Parameters); State.Invocations.Add(new FunctionAnalysis1stPass.Invocation( GetParentNodeIndices(), StatementIndex, NodeIndex, (JSVariable)null, jsm, newexp.ConstructorReference, variables )); } VisitChildren(newexp); }
public Invocation( NodeIndices parentNodeIndices, int statementIndex, int nodeIndex, JSType type, JSMethod method, object nonJSMethod, Dictionary <string, ArraySegment <string> > variables ) : base(parentNodeIndices, statementIndex, nodeIndex) { ThisType = type; ThisVariable = null; Method = method; if (method == null) { NonJSMethod = nonJSMethod; } else { NonJSMethod = null; } Variables = variables; }
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 { entry.SecondPass = new FunctionAnalysis2ndPass(this, GetFirstPass(id)); } } return(entry.SecondPass); }
/// <summary> /// Writes an interface member reference to the output. /// </summary> public void WriteInterfaceMemberToOutput( JavascriptFormatter output, Compiler.Extensibility.IAstEmitter astEmitter, JSFunctionExpression enclosingFunction, JSMethod jsMethod, JSExpression method, TypeReferenceContext referenceContext ) { int index; var record = new CachedInterfaceMemberRecord(jsMethod.Reference.DeclaringType, jsMethod.Identifier); if ((enclosingFunction.Method != null) || (enclosingFunction.Method.Method != null)) { var functionIdentifier = enclosingFunction.Method.Method.Identifier; CacheSet localSignatureSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSignatureSet)) { if (localSignatureSet.InterfaceMembers.TryGetValue(record, out index)) { output.WriteRaw("$im{0:X2}", index); return; } } } if (!Global.InterfaceMembers.TryGetValue(record, out index)) { output.Identifier(jsMethod.Reference.DeclaringType, referenceContext, false); output.Dot(); astEmitter.Emit(method); } else { output.WriteRaw("$IM{0:X2}()", index); } }
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); }
private static string GenerateMethodCall(ClassDeclarationSyntax classSyntax, JSMethod method, int argsLength) { var result = new StringBuilder(); if (method.ReturnType != "void") { result.Append("return "); } if (!method.IsStatic) { result.Append($"(({classSyntax.Identifier.ToString()})thisObj)."); } result.Append(method.MethodName); result.Append('('); int skipCount = 0; bool appendComma = false; if (method.HasEngineParameter) { appendComma = true; result.Append("engine"); skipCount++; } if (method.HasThisObject) { if (appendComma) { result.Append(", "); } appendComma = true; result.Append(ConvertTo($"thisObj", method.ThisObjectParameterType, null)); skipCount++; } int argIndex = 0; foreach (var parameter in method.Parameters) { if (appendComma) { result.Append(", "); } appendComma = true; if (argIndex < argsLength) { result.Append(ConvertTo($"args[{argIndex}]", parameter.Type, parameter.DefaultValue, argIndex)); } else { // Pass through undefined. if (parameter.DefaultValue != null) { result.Append(parameter.DefaultValue); } else { switch (parameter.Type) { case "object": result.Append("Undefined.Value"); break; case "bool": result.Append("false"); break; case "int": result.Append("0"); break; case "string": result.Append("\"undefined\""); break; case "double": result.Append($"double.NaN"); break; case "ObjectInstance": case "FunctionInstance": case "ArrayBufferInstance": case "SymbolInstance": return("throw new JavaScriptException(engine, ErrorType.TypeError, \"undefined cannot be converted to an object\");"); case "object[]": result.Append("new object[0]"); break; case "string[]": result.Append("new string[0]"); break; case "double[]": result.Append("new double[0]"); break; default: throw new InvalidOperationException($"Unsupported parameter type {parameter.Type}"); } } } argIndex++; } result.Append(");"); if (method.ReturnType == "void") { result.Append(" return Undefined.Value;"); } return(result.ToString()); }
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 )); }
public CachedMethodRecord(JSMethod method, int index) { Method = method; Index = index; }
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); }