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 void EmitEquals(Seq<JST.Statement> body, JST.Expression lhs) { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var left = parameters[0].ToE(); parameters.Add(innerNameSupply.GenSym()); var right = parameters[1].ToE(); var innerBody = new Seq<JST.Statement>(); var iequatableTypeRef = Env.Global.IEquatableTypeConstructorRef.ApplyTo(TypeCompEnv.TypeRef); var hasIEquatable = TypeCompEnv.TypeRef.IsAssignableTo(TypeCompEnv, iequatableTypeRef); var s = TypeCompEnv.Type.Style; 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.BinaryExpression(left, JST.BinaryOp.Equals, right))); else if (s is CST.HandleTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(left), new JST.Statements(new JST.ReturnStatement(JST.Expression.IsNull(right))))); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(right), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); innerBody.Add (new JST.ReturnStatement(new JST.BinaryExpression(left, JST.BinaryOp.StrictEquals, right))); } else if (s is CST.NullableTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(left), new JST.Statements(new JST.ReturnStatement(JST.Expression.IsNull(right))))); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(right), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); innerBody.Add (new JST.ReturnStatement (JST.Expression.DotCall (TypeCompEnv.ResolveType(TypeCompEnv.TypeBoundArguments[0], TypePhase.Slots), Constants.TypeEquals, left, right))); } else if (s is CST.StructTypeStyle) { if (hasIEquatable) { // Defer to IEquatable<T>::Equals var paramTypeRef = new CST.ParameterTypeRef(CST.ParameterFlavor.Type, 0); var equalsRef = new CST.MethodRef (iequatableTypeRef, "Equals", false, null, new Seq<CST.TypeRef> { paramTypeRef, paramTypeRef }, Env.Global.BooleanRef); var leftPtr = JST.Expression.DotCall (RootId.ToE(), Constants.RootNewPointerToValue, left, lhs); var call = Env.JSTHelpers.DefaultVirtualMethodCallExpression (TypeCompEnv, innerNameSupply, innerBody, equalsRef, new Seq<JST.Expression> { leftPtr, right }); innerBody.Add(new JST.ReturnStatement(call)); } else { foreach (var fieldDef in Parent.Fields.Where(f => !f.IsStatic)) { var fieldRef = new CST.FieldRef(TypeCompEnv.TypeRef, fieldDef.FieldSignature); var leftField = Env.JSTHelpers.ResolveInstanceField(TypeCompEnv, left, fieldRef); var rightField = Env.JSTHelpers.ResolveInstanceField(TypeCompEnv, right, fieldRef); innerBody.Add (new JST.IfStatement (JST.Expression.Not (JST.Expression.DotCall (TypeCompEnv.ResolveType(fieldDef.FieldType, TypePhase.Slots), Constants.TypeEquals, leftField, rightField)), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); } innerBody.Add(new JST.ReturnStatement(new JST.BooleanLiteral(true))); } } else if (s is CST.ObjectTypeStyle || (s is CST.ClassTypeStyle & hasIEquatable)) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(left), new JST.Statements(new JST.ReturnStatement(JST.Expression.IsNull(right))))); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(right), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false))))); var equalsRef = default(CST.MethodRef); if (hasIEquatable) { // Defer to IEquatable<T>::Equals var paramTypeRef = new CST.ParameterTypeRef(CST.ParameterFlavor.Type, 0); var iequatableSelfTypeRef = Env.Global.IEquatableTypeConstructorRef.ApplyTo(paramTypeRef); equalsRef = new CST.MethodRef (iequatableTypeRef, "Equals", false, null, new Seq<CST.TypeRef> { iequatableSelfTypeRef, paramTypeRef }, Env.Global.BooleanRef); } else { // Defer to Object::Equals virtual equalsRef = new CST.MethodRef (Env.Global.ObjectRef, "Equals", false, null, new Seq<CST.TypeRef> { Env.Global.ObjectRef, Env.Global.ObjectRef }, Env.Global.BooleanRef); } var call = Env.JSTHelpers.DefaultVirtualMethodCallExpression (TypeCompEnv, innerNameSupply, innerBody, equalsRef, new Seq<JST.Expression> { left, right }); innerBody.Add(new JST.ReturnStatement(call)); } else // default is ok return; body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeEquals, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); }
private void EmitHash(Seq<JST.Statement> body, JST.Expression lhs) { var innerNameSupply = NameSupply.Fork(); var parameters = new Seq<JST.Identifier>(); parameters.Add(innerNameSupply.GenSym()); var obj = parameters[0].ToE(); var innerBody = new Seq<JST.Statement>(); var s = TypeCompEnv.Type.Style; 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(obj)); else if (s is CST.HandleTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(obj), new JST.Statements(new JST.ReturnStatement(new JST.NumericLiteral(0))))); var objid = JST.Expression.Dot(obj, Constants.ObjectId); innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(objid), new JST.Statements(JST.Statement.Assignment (objid, new JST.UnaryExpression (JST.Expression.Dot(RootId.ToE(), Constants.RootNextObjectId), JST.UnaryOp.PostIncrement))))); innerBody.Add(new JST.ReturnStatement(objid)); } else if (s is CST.NullableTypeStyle) { innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(obj), new JST.Statements(new JST.ReturnStatement(new JST.NumericLiteral(0))))); innerBody.Add (new JST.ReturnStatement (JST.Expression.DotCall (TypeCompEnv.ResolveType(TypeCompEnv.TypeBoundArguments[0], TypePhase.Slots), Constants.TypeHash, obj))); } else if (s is CST.StructTypeStyle) { var hashId = innerNameSupply.GenSym(); innerBody.Add(JST.Statement.Var(hashId, new JST.NumericLiteral(0))); var hash = hashId.ToE(); foreach (var fieldDef in Parent.Fields.Where(f => !f.IsStatic)) { innerBody.Add (JST.Statement.Assignment (hash, new JST.BinaryExpression (new JST.BinaryExpression(hash, JST.BinaryOp.LeftShift, new JST.NumericLiteral(3)), JST.BinaryOp.BitwiseOR, new JST.BinaryExpression (hash, JST.BinaryOp.UnsignedRightShift, new JST.NumericLiteral(28))))); var fieldRef = new CST.FieldRef(TypeCompEnv.TypeRef, fieldDef.FieldSignature); var field = Env.JSTHelpers.ResolveInstanceField(TypeCompEnv, obj, fieldRef); innerBody.Add (JST.Statement.Assignment (hash, new JST.BinaryExpression (hash, JST.BinaryOp.BitwiseXOR, JST.Expression.DotCall (TypeCompEnv.ResolveType(fieldDef.FieldType, TypePhase.Slots), Constants.TypeHash, field)))); } innerBody.Add(new JST.ReturnStatement(hash)); } else if (s is CST.ObjectTypeStyle) { // NOTE: CLR Bizzarism: IEquatable<T> does not provide a GetHashCode, thus a // default EqualityComparer<T> when T has IEquatable<T> will use the IEquatable // Equals but the Object GetHashCode. Go figure. innerBody.Add (new JST.IfStatement (JST.Expression.IsNull(obj), new JST.Statements(new JST.ReturnStatement(new JST.NumericLiteral(0))))); var getHashCodeRef = new CST.MethodRef (Env.Global.ObjectRef, "GetHashCode", false, null, new Seq<CST.TypeRef> { Env.Global.ObjectRef }, Env.Global.Int32Ref); var call = Env.JSTHelpers.DefaultVirtualMethodCallExpression (TypeCompEnv, innerNameSupply, innerBody, getHashCodeRef, new Seq<JST.Expression> { obj }); innerBody.Add(new JST.ReturnStatement(call)); } else // Default is ok return; body.Add (JST.Statement.DotAssignment (lhs, Constants.TypeHash, new JST.FunctionExpression(parameters, new JST.Statements(innerBody)))); }