Skip to content
This repository has been archived by the owner on Aug 24, 2022. It is now read-only.

- TypeUtil.IsStructImmutable() added, [JSImmutable] works with readonly/structs and more #516

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
29 changes: 28 additions & 1 deletion JSIL/AssemblyTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,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 @@ -2175,6 +2189,11 @@ where NeedsStaticConstructor(f.FieldType)
output.NewLine();
astEmitter.Visit(expr);

//if (typedef.ToString() == "System.TimeSpan")
//{
//Console.WriteLine(fd.FullName); // detect TimeSpan.MaxValue issue
//}

TranslateCustomAttributes(context, typedef, fd, astEmitter, output);

output.Semicolon(false);
Expand Down Expand Up @@ -2313,6 +2332,10 @@ private void TranslateCustomAttributes (
bool isFirst = true;

foreach (var attribute in member.CustomAttributes) {
if (IsAttributeIgnored(attribute.AttributeType.FullName))
{
continue;
}
if (!isFirst || standalone)
output.NewLine();

Expand Down Expand Up @@ -2356,10 +2379,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
2 changes: 2 additions & 0 deletions JSIL/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public sealed class CodeGeneratorConfiguration {
public bool? AutoGenerateEventAccessorsInSkeletons;
public bool? AggressivelyUseElementProxies;
public bool? EmitAllParameterNames;
public readonly List<string> EmitAttributes = new List<string>();

public void MergeInto (CodeGeneratorConfiguration result) {
if (EliminateStructCopies.HasValue)
Expand Down Expand Up @@ -86,6 +87,7 @@ public void MergeInto (CodeGeneratorConfiguration result) {
result.AggressivelyUseElementProxies = AggressivelyUseElementProxies;
if (EmitAllParameterNames.HasValue)
result.EmitAllParameterNames = EmitAllParameterNames;
result.EmitAttributes.AddRange(EmitAttributes);
}
}

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 @@ -329,7 +329,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
11 changes: 11 additions & 0 deletions JSIL/TypeUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ public static bool IsStruct (TypeReference type) {
return (etype == MetadataType.ValueType);
}

public static bool IsStructImmutable(TypeReference type)
{
bool isImmutable = false;
TypeDefinition typeDef = type.Resolve();
if (typeDef.CustomAttributes.Count > 0)
{
isImmutable = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "JSIL.Meta.JSImmutable") != null;
}
return isImmutable;
}

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

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
39 changes: 39 additions & 0 deletions Libraries/JSIL.Core.js
Original file line number Diff line number Diff line change
Expand Up @@ -8313,6 +8313,45 @@ JSIL.Array.ShallowCopy = function (destination, source) {
JSIL.Array.CopyTo(source, destination, 0);
};

JSIL.Array.FindIndex = function (array, predicate) { // implements: int System.Array.FindIndex<T>(T[] array, Predicate<T> match)
for (var i = 0, l = array.length; i < l; i++) {
if (predicate(array[i]))
return i;
}
return -1;
};

JSIL.Array.Find = function (array, predicate) { // implements: T System.Array.Find<T>(T[] array, Predicate<T> match)
for (var i = 0, l = array.length; i < l; i++) {
if (predicate(array[i]))
return array[i];
}
return null; // use 'null' and not 'undefined'
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be JSIL.DefaultValue(T)?

};

JSIL.Array.ForEach = function (array, action) { // implements: void ForEach<T>(T[] array, Action<T> action)
if (JSIL.IsTypedArray(array)) {
for (var i = 0, l = array.length; i < l; i++) {
array[i] = action(array[i]);
}
}
else Array.prototype.forEach.call(array, action);
Copy link
Member

Choose a reason for hiding this comment

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

I think it's actually superior to always use the for loop. In many cases a 'self-hosted' JS loop is superior in performance to a native, so forEach is unlikely to be better here. The branch also makes the method harder for a JIT to optimize.

};

JSIL.Array.ConvertAll = function (array, converter) { // implements: TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter)
var cloned;
if (JSIL.IsTypedArray(array)) {
var ctor = Object.getPrototypeOf(array).constructor; // clone the typed array
cloned = new ctor(array);
for (var i = 0, l = cloned.length; i < l; i++) {
cloned[i] = converter(cloned[i]);
}
}
else cloned = Array.prototype.map.call(array, converter);
Copy link
Member

Choose a reason for hiding this comment

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

Same as above. Let's always use the loop here.

Also, I think you want to construct the result array using a length explicitly, then fill it with converted values. (It looks like you pass 'array' to the constructor instead)

Copy link
Author

Choose a reason for hiding this comment

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

I will fix them all and recommit, make all self-hosted loops.
"new ctor(array)", it's simply a copy constructor call, similar use is in JSIL.Array.Clone() method.


return cloned;
};

$jsilcore.CheckDelegateType = function (value) {
if (value === null)
return false;
Expand Down
4 changes: 2 additions & 2 deletions Meta/Builtins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ public static T CreateNamedFunction<T> (
throw new NotImplementedException("Not available outside JS");
}

public static bool IsTruthy (dynamic value) {
public static bool IsTruthy (object valueExplicitCastToObject) {
throw new NotImplementedException("Not available outside JS");
}

public static bool IsFalsy (dynamic value) {
public static bool IsFalsy (object valueExplicitCastToObject) {
throw new NotImplementedException("Not available outside JS");
}

Expand Down
9 changes: 9 additions & 0 deletions Meta/Meta.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
<PropertyGroup>
<ApplicationIcon>jsil.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>jsil.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
Expand All @@ -57,6 +63,9 @@
<ItemGroup>
<Content Include="jsil.ico" />
</ItemGroup>
<ItemGroup>
<None Include="jsil.snk" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
Binary file added Meta/jsil.snk
Binary file not shown.
31 changes: 30 additions & 1 deletion Proxies/Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,36 @@ public static int IndexOf<T> (T[] array, T value, int startIndex) {
throw new InvalidOperationException();
}

[JSReplacement("JSIL.Array.Find($array, $match)")]
public static T Find<T>(T[] array, Predicate<T> match)
{
throw new InvalidOperationException();
}

[JSReplacement("JSIL.Array.FindIndex($array, $match)")]
public static int FindIndex<T>(T[] array, Predicate<T> match)
{
throw new InvalidOperationException();
}

[JSReplacement("JSIL.Array.ForEach($array, $action)")]
public static void ForEach<T>(T[] array, Action<T> action)
{
throw new InvalidOperationException();
}

[JSReplacement("JSIL.Array.ConvertAll($array, $converter)")]
public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter)
{
throw new InvalidOperationException();
}

[JSReplacement("Array.prototype.every.call($array, $match)")]
public static bool TrueForAll<T>(T[] array, Predicate<T> match)
{
throw new InvalidOperationException();
}

[JSReplacement("JSIL.Array.Clone($this)")]
public object Clone () {
throw new InvalidOperationException();
Expand Down Expand Up @@ -104,6 +134,5 @@ public static void Sort<T> (T[] array) {
public System.Collections.IEnumerator GetEnumerator () {
throw new InvalidOperationException();
}

}
}
23 changes: 21 additions & 2 deletions Proxies/Numbers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,28 @@ public string ToString (string format, IFormatProvider formatProvider) {
throw new InvalidOperationException();
}

[JSReplacement("!isFinite($value)")]
public static bool IsInfinity(NumberProxy value)
{
throw new InvalidOperationException();
}

[JSReplacement("isNaN($value)")]
public static bool IsNaN (NumberProxy value) {
throw new InvalidOperationException();
public static bool IsNaN(NumberProxy value)
{
throw new InvalidOperationException();
}

[JSReplacement("($value == Infinity)")]
public static bool IsPositiveInfinity(NumberProxy value)
{
throw new InvalidOperationException();
}

[JSReplacement("($value == -Infinity)")]
public static bool IsNegativeInfinity(NumberProxy value)
{
throw new InvalidOperationException();
}

[JSIsPure]
Expand Down