Skip to content
This repository was archived by the owner on Aug 24, 2022. It is now read-only.
2 changes: 1 addition & 1 deletion JSIL/AST/JSExpressionTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1981,7 +1981,7 @@ internal static bool AllowBizarreReferenceCast (TypeReference currentType, TypeR
return true;
}

if (TypeUtil.IsEnum(currentType) && TypeUtil.IsIntegral(newType))
if (TypeUtil.IsEnum(currentType) > 0 && TypeUtil.IsIntegral(newType))
return true;

// Allow casting T*& to U*&
Expand Down
45 changes: 43 additions & 2 deletions JSIL/AssemblyTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ public AssemblyTranslator (
Configuration = configuration;
bool useDefaultProxies = configuration.UseDefaultProxies.GetValueOrDefault(true);

TypeUtil.s_ConfigurationChangeEnumToNumber = configuration.CodeGenerator.ChangeEnumToNumber.GetValueOrDefault(0);

Manifest = manifest ?? new AssemblyManifest();

if (typeInfoProvider != null) {
Expand Down Expand Up @@ -294,6 +296,20 @@ protected bool IsRedirected (string assemblyName) {
return false;
}

protected bool IsAttributeIgnored(string attributeName)
{
List<string> emitAttributes = Configuration.CodeGenerator.EmitAttributes;
if (emitAttributes.Count == 0)
return false;
foreach (var ia in emitAttributes)
{
if (Regex.IsMatch(attributeName, ia, RegexOptions.IgnoreCase))
return false;
}

return true;
}

public string ClassifyAssembly (AssemblyDefinition asm) {
if (IsIgnored(asm.FullName))
return "ignored";
Expand Down Expand Up @@ -1153,6 +1169,18 @@ protected void DeclareType (
return;
}

bool isAttribute = false;
TypeInfo typeBaseClass = typeInfo.BaseClass;
while (!isAttribute && typeBaseClass != null)
{
isAttribute = (typeBaseClass.FullName == "System.Attribute");
typeBaseClass = typeBaseClass.BaseClass;
}
if (isAttribute && IsAttributeIgnored(typeInfo.FullName))
{
return;
}

// This type is defined in JSIL.Core so we don't want to cause a name collision.
if (!ShouldGenerateTypeDeclaration(typedef, makingSkeletons)) {
declaredTypes.Add(typedef);
Expand Down Expand Up @@ -1219,6 +1247,11 @@ protected void DeclareType (
TranslateInterface(context, astEmitter, output, typedef);
return;
} else if (typedef.IsEnum) {
if ((TypeUtil.IsEnum(typedef) & TypeUtil.EnumKind.ChangeToNumber) != 0)
{
return;
}

output.Comment("enum {0}", Util.DemangleCecilTypeName(typedef.FullName));
output.NewLine();
output.NewLine();
Expand Down Expand Up @@ -2416,7 +2449,7 @@ private JSExpression TranslateAttributeConstructorArgument (
);
} else if (ca.Type.FullName == "System.Type") {
return new JSTypeOfExpression((TypeReference)ca.Value);
} else if (TypeUtil.IsEnum(ca.Type)) {
} else if (TypeUtil.IsEnum(ca.Type) > 0) {
var longValue = Convert.ToInt64(ca.Value);
var result = JSEnumLiteral.TryCreate(
_TypeInfoProvider.GetExisting(ca.Type),
Expand Down Expand Up @@ -2457,6 +2490,10 @@ private void TranslateCustomAttributes (
if (ShouldSkipMember(attribute.AttributeType))
continue;

if (IsAttributeIgnored(attribute.AttributeType.FullName))
{
continue;
}
if (!isFirst || standalone)
output.NewLine();

Expand Down Expand Up @@ -2500,10 +2537,14 @@ private void TranslateParameterAttributes (
JavascriptAstEmitter astEmitter,
JavascriptFormatter output
) {
if (!Configuration.CodeGenerator.EmitAllParameterNames.GetValueOrDefault(false))
{
return;
}
output.Indent();

foreach (var parameter in method.Parameters) {
if (!parameter.HasCustomAttributes && !Configuration.CodeGenerator.EmitAllParameterNames.GetValueOrDefault(false))
if (!parameter.HasCustomAttributes)
continue;

output.NewLine();
Expand Down
5 changes: 5 additions & 0 deletions JSIL/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public sealed class CodeGeneratorConfiguration {
public bool? AutoGenerateEventAccessorsInSkeletons;
public bool? AggressivelyUseElementProxies;
public bool? EmitAllParameterNames;
public int? ChangeEnumToNumber; // 0 - disabled, 1 - with attribute only, 2 - all enums
public readonly List<string> EmitAttributes = new List<string>();

public void MergeInto (CodeGeneratorConfiguration result) {
if (EliminateStructCopies.HasValue)
Expand Down Expand Up @@ -89,6 +91,9 @@ public void MergeInto (CodeGeneratorConfiguration result) {
result.AggressivelyUseElementProxies = AggressivelyUseElementProxies;
if (EmitAllParameterNames.HasValue)
result.EmitAllParameterNames = EmitAllParameterNames;
if (ChangeEnumToNumber.HasValue)
result.ChangeEnumToNumber = ChangeEnumToNumber;
result.EmitAttributes.AddRange(EmitAttributes);
}
}

Expand Down
6 changes: 4 additions & 2 deletions JSIL/ILBlockTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ protected JSExpression Translate_UnaryOp (ILExpression node, JSUnaryOperator op)
}

// Insert correct casts when unary operators are applied to enums.
if (TypeUtil.IsEnum(innerType) && TypeUtil.IsEnum(node.InferredType ?? node.ExpectedType)) {
if (TypeUtil.IsEnum(innerType) > 0 && TypeUtil.IsEnum(node.InferredType ?? node.ExpectedType) > 0) {
return JSCastExpression.New(
new JSUnaryOperatorExpression(
op,
Expand Down Expand Up @@ -565,6 +565,8 @@ protected JSExpression DoMethodReplacement (
case "System.Boolean JSIL.Builtins::IsFalsy(System.Object)":
return new JSUnaryOperatorExpression(JSOperator.LogicalNot, arguments.First(), TypeSystem.Boolean);

case "T JSIL.Verbatim::Expression(System.String)":
case "T JSIL.Verbatim::Expression(System.String,System.Object[])":
case "System.Object JSIL.Verbatim::Expression(System.String)":
case "System.Object JSIL.Verbatim::Expression(System.String,System.Object[])": {
var expression = arguments[0] as JSStringLiteral;
Expand Down Expand Up @@ -1731,7 +1733,7 @@ protected JSTernaryOperatorExpression Translate_TernaryOp (ILExpression node) {
}

protected JSExpression Translate_Mul (ILExpression node) {
if (TypeUtil.IsIntegral(node.ExpectedType)) {
if (node.ExpectedType != null && TypeUtil.IsIntegral(node.ExpectedType)) {
var left = TranslateNode(node.Arguments[0]);
var right = TranslateNode(node.Arguments[1]);
var leftType = left.GetActualType(TypeSystem);
Expand Down
27 changes: 24 additions & 3 deletions JSIL/JavascriptAstEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,12 @@ public void VisitNode (JSAsExpression ae) {
}

public void VisitNode (JSCastExpression ce) {
if ((TypeUtil.IsEnum(ce.NewType) & TypeUtil.EnumKind.ChangeToNumber) != 0)
{
Visit(ce.Expression); // no cast here, since Enum changed to JS number
return;
}

IncludeTypeParens.Push(false);
try {
WritePossiblyCachedTypeIdentifier(ce.NewType, ce.CachedTypeIndex);
Expand Down Expand Up @@ -817,6 +823,20 @@ public void VisitNode (JSBooleanLiteral b) {
}

public void VisitNode (JSEnumLiteral enm) {
TypeUtil.EnumKind enumKind = TypeUtil.IsEnum(enm.EnumType);
if ((enumKind & TypeUtil.EnumKind.ChangeToNumber) != 0) {
if (enm.Names.Length == 1) {
Output.Comment(enm.EnumType.Name + "." + enm.Names[0]);
}
if ((enumKind & TypeUtil.EnumKind.FlagIsFlags) != 0) {
Output.WriteRaw("0x{0:X}", enm.Value);
}
else {
Output.Value(enm.Value);
}
return;
}

if (enm.CachedEnumType != null)
Visit(enm.CachedEnumType);
else
Expand Down Expand Up @@ -915,7 +935,7 @@ public void VisitNode (JSIgnoredTypeReference itr) {
}

public void VisitNode (JSDefaultValueLiteral defaultValue) {
if (TypeUtil.IsEnum(defaultValue.Value)) {
if ((TypeUtil.IsEnum(defaultValue.Value) & TypeUtil.EnumKind.IsEnum) != 0) {
EnumMemberInfo emi;
var enumInfo = TypeInfo.Get(defaultValue.Value);

Expand Down Expand Up @@ -1665,8 +1685,9 @@ private bool NeedTruncationForBinaryOperator (JSBinaryOperatorExpression bop, Ty
public void VisitNode (JSBinaryOperatorExpression bop) {
var resultType = bop.GetActualType(TypeSystem);

TypeReference resultTypeStripped = TypeUtil.StripNullable(resultType);
bool needsCast = (bop.Operator is JSArithmeticOperator) &&
TypeUtil.IsEnum(TypeUtil.StripNullable(resultType));
(TypeUtil.IsEnum(resultTypeStripped) & TypeUtil.EnumKind.IsEnum) != 0;
bool needsTruncation = NeedTruncationForBinaryOperator(bop, resultType);
bool parens = NeedParensForBinaryOperator(bop);
var parenCount = GetParenCountForTruncation(resultType);
Expand All @@ -1675,7 +1696,7 @@ public void VisitNode (JSBinaryOperatorExpression bop) {
if (bop.Operator is JSAssignmentOperator)
throw new NotImplementedException("Truncation of assignment operations not implemented");
} else if (needsCast) {
Output.Identifier(TypeUtil.StripNullable(resultType), ReferenceContext);
Output.Identifier(resultTypeStripped, ReferenceContext);
Output.WriteRaw(".$Cast");
}

Expand Down
12 changes: 12 additions & 0 deletions JSIL/JavascriptFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,12 @@ public void TypeReference (TypeReference type, TypeReferenceContext context) {
}
}

if ((TypeUtil.IsEnum(type) & TypeUtil.EnumKind.ChangeToNumber) != 0)
{
WriteRaw("$.Int32");
return;
}

if (type.FullName == "JSIL.Proxy.AnyType") {
Value("JSIL.AnyType");
return;
Expand Down Expand Up @@ -862,6 +868,12 @@ protected void TypeIdentifier (TypeReference type, TypeReferenceContext context,
return;
}

if ((TypeUtil.IsEnum(type) & TypeUtil.EnumKind.ChangeToNumber) != 0)
{
WriteRaw("$jsilcore.System.Int32");
return;
}

var typedef = type.Resolve();
if (typedef != null) {
if (GetContainingAssemblyName(typedef) == Assembly.FullName) {
Expand Down
3 changes: 3 additions & 0 deletions JSIL/Transforms/CacheTypeExpressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ public bool IsCacheable (TypeReference type) {
if (TypeUtil.IsOpenType(type))
return false;

if ((TypeUtil.IsEnum(type) & TypeUtil.EnumKind.ChangeToNumber) != 0)
return false;

return true;
}

Expand Down
4 changes: 2 additions & 2 deletions JSIL/Transforms/IntroduceEnumCasts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static JSExpression CastToEnumType (JSExpression value, TypeReference typ
public static bool IsNullableEnum (TypeReference tr) {
var git = tr as GenericInstanceType;
if ((git != null) && (git.Name == "Nullable`1")) {
if (TypeUtil.IsEnum(git.GenericArguments[0]))
if ((TypeUtil.IsEnum(git.GenericArguments[0]) & TypeUtil.EnumKind.IsEnum) != 0)
return true;
}

Expand All @@ -89,7 +89,7 @@ public static bool IsNullableEnum (TypeReference tr) {
public static bool IsEnumOrNullableEnum (TypeReference tr) {
tr = TypeUtil.DereferenceType(tr, false);

if (TypeUtil.IsEnum(tr))
if ((TypeUtil.IsEnum(tr) & TypeUtil.EnumKind.IsEnum) != 0)
return true;

return IsNullableEnum(tr);
Expand Down
2 changes: 1 addition & 1 deletion JSIL/Transforms/StaticAnalysis/EmulateStructAssignment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ public void VisitNode (JSInvocationExpression invocation) {
// before we call it.
var thisReferenceType = thisReference.GetActualType(TypeSystem);

if (TypeUtil.IsStruct(thisReferenceType)) {
if (TypeUtil.IsStruct(thisReferenceType) && !TypeUtil.IsStructImmutable(thisReferenceType)) {
if ((thisReference is JSVariable) || (thisReference is JSFieldAccess)) {
var rre = ParentNode as JSResultReferenceExpression;
var cloneExpr = new JSBinaryOperatorExpression(
Expand Down
52 changes: 48 additions & 4 deletions JSIL/TypeUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static bool IsStruct (TypeReference type) {
type = DereferenceType(type);
MetadataType etype = type.MetadataType;

if (IsEnum(type))
if (IsEnum(type) > 0)
return false;

var git = type as GenericInstanceType;
Expand All @@ -96,6 +96,27 @@ public static bool IsStruct (TypeReference type) {
return (etype == MetadataType.ValueType);
}

public static bool IsStructImmutable(TypeReference type) // much faster than: typeInfo = TypeInfo.GetTypeInformation(type) + typeInfo.IsImmutable
{
bool isImmutable = false;
TypeDefinition typeDef = type.Resolve();
if (typeDef == null)
{
return false;
}
if (typeDef.CustomAttributes != null &&
typeDef.CustomAttributes.Count > 0)
{ // check the attribute first
isImmutable = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "JSIL.Meta.JSImmutable") != null;
}
if (!isImmutable)
{ // all fields are read-only
isImmutable = typeDef.Fields.All(fd => fd.IsStatic || fd.IsInitOnly);
}

return isImmutable;
}

public static bool IsNumeric (TypeReference type) {
type = DereferenceType(type);

Expand Down Expand Up @@ -247,9 +268,32 @@ public static TypeReference StripNullable (TypeReference type) {
return type;
}

public static bool IsEnum (TypeReference type) {
var typedef = GetTypeDefinition(type);
return (typedef != null) && (typedef.IsEnum);
[Flags]
public enum EnumKind {
ChangeToNumber = 0x01,
IsEnum = 0x02,

FlagIsFlags = 0x04
};

internal static int s_ConfigurationChangeEnumToNumber = 0; // 0 - disabled, 1 - with attribute only, 2 - all enums

public static EnumKind IsEnum(TypeReference type) { // checks if type is Enum and should be converted to JS number
var typedef = GetTypeDefinition(type);
bool isEnum = (typedef != null) && typedef.IsEnum;
EnumKind result = 0;
if (isEnum)
{
var attributes = typedef.CustomAttributes;
result = (s_ConfigurationChangeEnumToNumber == 2) ||
(s_ConfigurationChangeEnumToNumber > 0 &&
attributes.Count > 0 &&
attributes.Any(ca => ca.AttributeType.FullName == "JSIL.Meta.JSChangeEnumToNumber")) ? EnumKind.ChangeToNumber : EnumKind.IsEnum;
result |= (attributes.Count > 0 &&
attributes.Any(ca => ca.AttributeType.FullName == "System.FlagsAttribute")) ? EnumKind.FlagIsFlags : 0;
}

return result;
}

public static bool IsBoolean (TypeReference type) {
Expand Down
7 changes: 4 additions & 3 deletions JSIL_NoXNA.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2010
# Visual Studio 2013
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Compiler", "Compiler\Compiler.csproj", "{C7BF4561-20DD-4E49-8B5A-4E8AF032C47A}"
ProjectSection(ProjectDependencies) = postProject
{984CC812-9470-4A13-AFF9-CC44068D666C} = {984CC812-9470-4A13-AFF9-CC44068D666C}
Expand Down Expand Up @@ -72,8 +74,7 @@ Global
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Debug|x86.Build.0 = Debug|x86
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|.NET 4.5 x86.ActiveCfg = Release|.NET 4.5 x86
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|.NET 4.5 x86.Build.0 = Release|.NET 4.5 x86
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|Any CPU.Build.0 = Release|Any CPU
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|Any CPU.ActiveCfg = Release|x86
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|x86.ActiveCfg = Release|x86
{DA03D241-B70C-44D7-A465-3CEB5A9416AE}.Release|x86.Build.0 = Release|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|.NET 4.5 x86.ActiveCfg = Debug|Any CPU
Expand Down
7 changes: 7 additions & 0 deletions Libraries/JSIL.Bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,13 @@ JSIL.ImplementExternals(
}
);

$.Method({ Static: false, Public: true }, "GetType",
new JSIL.MethodSignature(mscorlib.TypeRef("System.Type"), []),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recently introduced MethodSignature.Return(T) is appropriate for signatures like this when writing new code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should I use it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MethodSignature.Return(T) === new JSIL.MethodSignature(T, [], []), but the instance is cached so memory usage is lower and invocation thunks are shared

function () {
return this.__ThisType__;
}
);

$.Method({Static: false, Public: true }, "toString",
new JSIL.MethodSignature($.String, []),
function () {
Expand Down
Loading