public void BindUsage(ISeq <JST.Statement> statements, CST.Usage usage, TypePhase typePhase) { foreach (var kv in usage.Assemblies) { if (kv.Value > 1) { if (!boundAssemblies.ContainsKey(kv.Key)) { var e = env.JSTHelpers.DefaultResolveAssembly(this, kv.Key); if (e != null) { if (env.DebugMode) { statements.Add(new JST.CommentStatement(kv.Key.ToString())); } var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); boundAssemblies.Add(kv.Key, id.ToE()); } } // else: use outer binding } // else: inline expression as need it } foreach (var kv in usage.Types) { if (kv.Value > 1) { var existing = default(ExpressionAndPhase); var b = boundTypes.TryGetValue(kv.Key, out existing); if (!b || typePhase > existing.Phase) { var e = env.JSTHelpers.DefaultResolveType(this, kv.Key, typePhase); if (e != null) { if (env.DebugMode) { statements.Add(new JST.CommentStatement(kv.Key.ToString())); } var id = NameSupply.GenSym(); statements.Add(JST.Statement.Var(id, e)); var updated = new ExpressionAndPhase(id.ToE(), typePhase); if (b) { boundTypes[kv.Key] = updated; } else { boundTypes.Add(kv.Key, updated); } } } // else: use outer binding } // else: inline expression as need it } }
// ---------------------------------------------------------------------- // Type structures // ---------------------------------------------------------------------- // Collect usage statistics for all the types we'll need at phase 1: // - Any type args to the base type constructor (for BaseType field) // - All types which are assignable from this type (for Supertypes field) // - All interface types for interface methods implemented implicitly or explicit by this type // (will be subset of above supertypes, but we collect so as to see which types are used more than once). private CST.Usage CollectPhase1Usage() { var usage = new CST.Usage(); foreach (var typeRef in TypeCompEnv.AllExtendedTypes().Concat(TypeCompEnv.AllImplementedTypes())) typeRef.AccumUsage(usage, true); var realTypeDef = TypeCompEnv.Type as CST.RealTypeDef; if (realTypeDef != null) { foreach (var kv in realTypeDef.SlotImplementations) { if (kv.Key.DefiningType.Style(TypeCompEnv) is CST.IntNativeTypeStyle) TypeCompEnv.SubstituteType(kv.Key.DefiningType).AccumUsage(usage, true); } } return usage; }
public void BindUsage(ISeq <JST.Statement> statements, CST.Usage usage) { BindMap (statements, usage.Assemblies, boundAssemblies, n => n.ToString(), n => env.JSTHelpers.DefaultResolveAssembly(this, n)); BindMap (statements, usage.Types, boundTypes, t => t.ToString(), t => env.JSTHelpers.DefaultResolveType(this, t, TypePhase.Constructed)); BindMap (statements, usage.VariablePointers, boundVariablePointers, id => String.Format("Pointer to {0}", id.Value), VariablePointerExpression); }
// For speed we inline rather than chain to base-type initialize object functions private JST.FunctionExpression ConstructObjectFunction() { var parameters = new Seq<JST.Identifier>(); var innerBody = new Seq<JST.Statement>(); if (TypeCompEnv.Type.Style is CST.ClassTypeStyle) { var fieldRefs = new Seq<CST.FieldRef>(); AccumInstanceFields(TypeCompEnv, fieldRefs); var innerTypeCompEnv = TypeCompEnv.EnterFunction(); var usage = new CST.Usage(); var trivFieldRefs = new Seq<CST.FieldRef>(); var nonTrivFieldRefs = new Seq<CST.FieldRef>(); foreach (var fieldRef in fieldRefs) { if (Env.JSTHelpers.DefaultFieldValueIsNonNull(innerTypeCompEnv, fieldRef)) { nonTrivFieldRefs.Add(fieldRef); fieldRef.ExternalFieldType.AccumUsage(usage, true); } else trivFieldRefs.Add(fieldRef); } if (trivFieldRefs.Count + nonTrivFieldRefs.Count > 0) { var suppressInitId = innerTypeCompEnv.NameSupply.GenSym(); parameters.Add(suppressInitId); var ifBody = new Seq<JST.Statement>(); innerTypeCompEnv.BindUsage(ifBody, usage, TypePhase.Constructed); var inst = default(JST.Expression); if (trivFieldRefs.Count + nonTrivFieldRefs.Count > 1) { var instId = innerTypeCompEnv.NameSupply.GenSym(); ifBody.Add(JST.Statement.Var(instId, new JST.ThisExpression())); inst = instId.ToE(); } else inst = new JST.ThisExpression(); foreach (var fieldRef in nonTrivFieldRefs) { ifBody.Add (JST.Statement.DotAssignment (inst, Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false), Env.JSTHelpers.DefaultFieldValue(innerTypeCompEnv, fieldRef))); } if (trivFieldRefs.Count > 0) { var assn = (JST.Expression)new JST.NullExpression(); foreach (var fieldRef in trivFieldRefs) { var fld = JST.Expression.Dot (inst, Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false)); assn = new JST.BinaryExpression(fld, JST.BinaryOp.Assignment, assn); } ifBody.Add(new JST.ExpressionStatement(assn)); } // If constructor is being called to build a prototype object, don't initialize // any field, since // - they aren't needed // - we can't access the field types yet innerBody.Add(new JST.IfStatement(JST.Expression.Not(suppressInitId.ToE()), new JST.Statements(ifBody))); } } // else: for value types, the DefaultValue function is responsible for constructing values. // The ConstructObject function is used only when constructing pointers/boxes. return new JST.FunctionExpression(parameters, new JST.Statements(innerBody)); }
// For speed we inline rather than chain to base-type default values // (Though, actually, that's not necessary since value types are sealed and have not inherited fields...) private void EmitDefaultValue(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; var innerBody = new Seq<JST.Statement>(); if (s is CST.VoidTypeStyle || s is CST.ManagedPointerTypeStyle) innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); else if (s is CST.NumberTypeStyle || s is CST.EnumTypeStyle || TypeCompEnv.TypeRef.Equals(Env.Global.DecimalRef)) innerBody.Add(new JST.ReturnStatement(new JST.NumericLiteral(0))); else if (s is CST.StructTypeStyle) { var allFieldRefs = new Seq<CST.FieldRef>(); AccumInstanceFields(TypeCompEnv, allFieldRefs); var innerTypeCompEnv = TypeCompEnv.EnterFunction(); var usage = new CST.Usage(); foreach (var fieldRef in allFieldRefs) { if (Env.JSTHelpers.DefaultFieldValueIsNonNull(innerTypeCompEnv, fieldRef)) fieldRef.ExternalFieldType.AccumUsage(usage, true); } innerTypeCompEnv.BindUsage(innerBody, usage, TypePhase.Constructed); var bindings = new OrdMap<JST.Identifier, JST.Expression>(); foreach (var fieldRef in allFieldRefs) bindings.Add (Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false), Env.JSTHelpers.DefaultFieldValue(innerTypeCompEnv, fieldRef)); innerBody.Add(new JST.ReturnStatement(new JST.ObjectLiteral(bindings))); } // else: default default value of null is ok if (innerBody.Count > 0) body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeDefaultValue, new JST.FunctionExpression(null, new JST.Statements(innerBody)))); }
// For speed we inline rather than chain to base-type clones private void EmitMemberwiseClone(Seq<JST.Statement> body, JST.Expression lhs) { var s = TypeCompEnv.Type.Style; if (s is CST.ClassTypeStyle || s is CST.StructTypeStyle) { var fields = new Seq<CST.FieldRef>(); AccumInstanceFields(TypeCompEnv, fields); var trivFields = new Seq<CST.FieldRef>(); var nonTrivFields = new Seq<CST.FieldRef>(); foreach (var fieldRef in fields) { var fieldType = ((CST.FieldSignature)fieldRef.ExternalSignature).FieldType; if (Env.JSTHelpers.CloneIsNonTrivial(TypeCompEnv, fieldType)) nonTrivFields.Add(fieldRef); else trivFields.Add(fieldRef); } if (nonTrivFields.Count > 0) { var innerTypeCompEnv = TypeCompEnv.EnterFunction(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerTypeCompEnv.NameSupply.GenSym()); var oldObj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); var usage = new CST.Usage(); foreach (var fieldRef in nonTrivFields) fieldRef.ExternalFieldType.AccumUsage(usage, true); innerTypeCompEnv.BindUsage(innerBody, usage, TypePhase.Constructed); var newObjId = innerTypeCompEnv.NameSupply.GenSym(); if (s is CST.ClassTypeStyle) // Reference type, object Id is allocated lazily innerBody.Add (JST.Statement.Var (newObjId, JST.Expression.DotCall(TypeId.ToE(), Constants.TypeConstructObject))); else // Value type innerBody.Add(JST.Statement.Var(newObjId, new JST.ObjectLiteral())); var newObj = newObjId.ToE(); // Explicity clone non-trivial fields foreach (var fieldRef in nonTrivFields) { var fieldId = Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false); innerBody.Add (JST.Statement.DotAssignment (newObj, fieldId, Env.JSTHelpers.CloneExpressionForType (innerTypeCompEnv, fieldRef.ExternalFieldType, JST.Expression.Dot(oldObj, fieldId)))); } if (trivFields.Count < 3) { // Explicity copy the remaining trivial fields foreach (var fieldRef in trivFields) { var fieldId = Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, false); innerBody.Add (JST.Statement.DotAssignment(newObj, fieldId, JST.Expression.Dot(oldObj, fieldId))); } } else { // Generically copy the remaining trivial fields innerBody.Add (JST.Statement.DotCall(RootId.ToE(), Constants.RootInheritProperties, newObj, oldObj)); } innerBody.Add(new JST.ReturnStatement(newObj)); body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeMemberwiseClone, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); } else { // default generic clone with no inner cloning is ok return; } } else { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var oldObj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); if (s is CST.VoidTypeStyle) { innerBody.Add (new JST.ThrowStatement (JST.Expression.DotCall(RootId.ToE(), Constants.RootInvalidOperationException))); } else if (s is CST.ArrayTypeStyle) { var newObjId = innerNameSupply.GenSym(); innerBody.Add (JST.Statement.Var (newObjId, new JST.NewExpression (new JST.CallExpression (Constants.Array.ToE(), JST.Expression.Dot(oldObj, Constants.length))))); innerBody.Add(JST.Statement.DotAssignment(newObjId.ToE(), Constants.ObjectType, TypeId.ToE())); // Object Id is allocated lazily var iId = innerNameSupply.GenSym(); var loopClause = new JST.ForVarLoopClause (iId, new JST.BinaryExpression (iId.ToE(), JST.BinaryOp.LessThan, JST.Expression.Dot(oldObj, Constants.length)), new JST.UnaryExpression(iId.ToE(), JST.UnaryOp.PostIncrement)); var loopBody = JST.Statement.IndexAssignment (newObjId.ToE(), iId.ToE(), Env.JSTHelpers.CloneExpressionForType (TypeCompEnv, TypeCompEnv.TypeBoundArguments[0], new JST.IndexExpression(oldObj, iId.ToE()))); innerBody.Add(new JST.ForStatement(loopClause, new JST.Statements(loopBody))); innerBody.Add(new JST.ReturnStatement(newObjId.ToE())); } else if (s is CST.NullableTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(oldObj), new JST.Statements(new JST.ReturnStatement(new JST.NullExpression())), new JST.Statements(new JST.ReturnStatement (Env.JSTHelpers.CloneExpressionForType (TypeCompEnv, TypeCompEnv.TypeBoundArguments[0], oldObj))))); } else { innerBody.Add(new JST.ReturnStatement(oldObj)); } body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeMemberwiseClone, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); } }
private void EmitSetupType(Seq<JST.Statement> body, JST.Expression lhs) { // NOTE: We always emit the SetupType function, even if the body is empty, so that we can // track which types are at phase 3. (We need to initialize base types and type arguments // to phase 3 when type itself is brought up to phase 3.) // Collect static fields and their types // NOTE: We used to also bind null instance fields into the prototype, but this turns out to // be a significant performance hit var staticFields = new Seq<CST.FieldRef>(); var usage = new CST.Usage(); foreach (var fieldDef in Parent.Fields.Where(f => f.IsStatic)) { var fieldRef = new CST.FieldRef(TypeCompEnv.TypeRef, fieldDef.FieldSignature); if (TypeCompEnv.Type.Style is CST.EnumTypeStyle || fieldDef.Init == null || fieldDef.Init.Flavor != CST.FieldInitFlavor.Const) { staticFields.Add(fieldRef); if (Env.JSTHelpers.DefaultFieldValueIsNonNull(TypeCompEnv, fieldRef)) // We'll need type to construct default fieldRef.ExternalFieldType.AccumUsage(usage, true); } // else: constant static fields, other than enums, don't need any run-time representation } var innerBody = new Seq<JST.Statement>(); var innerTypeCompEnv = TypeCompEnv.EnterFunction(); innerTypeCompEnv.BindUsage(innerBody, usage, TypePhase.Constructed); if (staticFields.Count > 0) { if (Env.DebugMode) innerBody.Add(new JST.CommentStatement("Static fields")); foreach (var fieldRef in staticFields) { innerBody.Add (JST.Statement.DotAssignment (TypeId.ToE(), Env.JSTHelpers.ResolveFieldToIdentifier(innerTypeCompEnv, fieldRef, true), Env.JSTHelpers.DefaultFieldValue(innerTypeCompEnv, fieldRef))); } } if (Parent.StaticInitializer != null) { if (Env.DebugMode) innerBody.Add(new JST.CommentStatement("Static constructor")); innerBody.Add (new JST.ExpressionStatement (innerTypeCompEnv.MethodCallExpression (Parent.StaticInitializer, innerTypeCompEnv.NameSupply, false, JST.Constants.EmptyExpressions))); } EmitReflection(innerBody, innerTypeCompEnv, TypeId.ToE()); body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeSetupType, new JST.FunctionExpression(null, new JST.Statements(innerBody)))); }
private JST.FunctionExpression DelegateImporterExporter(bool isImporter) { var delTypeDef = (CST.DelegateTypeDef)TypeCompEnv.Type; var delInfo = Env.InteropManager.DelegateInfo(TypeCompEnv.Assembly, TypeCompEnv.Type); var func = isImporter ? Constants.RootDelegateImporter : Constants.RootDelegateExporter; var innerTypeCompEnv = TypeCompEnv.EnterFunction(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerTypeCompEnv.NameSupply.GenSym()); var body = new Seq<JST.Statement>(); var usage = new CST.Usage(); foreach (var p in delTypeDef.ValueParameters) TypeCompEnv.SubstituteType(p.Type).AccumUsage(usage, true); if (delTypeDef.Result != null) TypeCompEnv.SubstituteType(delTypeDef.Result.Type).AccumUsage(usage, true); innerTypeCompEnv.BindUsage(body, usage, TypePhase.Constructed); body.Add (new JST.ReturnStatement (JST.Expression.DotCall (RootId.ToE(), func, TypeId.ToE(), new JST.ArrayLiteral (delTypeDef.ValueParameters.Select (p => innerTypeCompEnv.ResolveType(p.Type, TypePhase.Constructed)).ToSeq()), delTypeDef.Result == null ? new JST.NullExpression() : innerTypeCompEnv.ResolveType(delTypeDef.Result.Type, TypePhase.Constructed), new JST.BooleanLiteral(delInfo.IsCaptureThis), new JST.BooleanLiteral(delInfo.IsInlineParamsArray), parameters[0].ToE()))); return new JST.FunctionExpression(parameters, new JST.Statements(body)); }