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

Commit 2b3d60d

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 4c6793c + ea06a95 commit 2b3d60d

22 files changed

Lines changed: 828 additions & 89 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,5 @@ pingme.txt
124124
bin/
125125
obj/
126126
*.userprefs
127+
desktop.ini
128+
/httpd

Build/Projects/JSIL.Tests.definition

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@
9292
<None Include="EmscriptenTestCases\PassPtrToPinnedArray.cs" />
9393
<None Include="AnalysisTestCases\Issue696.cs" />
9494
<None Include="EmscriptenTestCases\EnumRoundTrip.cs" />
95+
<None Include="AnalysisTestCases\FNABaseOffset.cs" />
9596
<Compile Include="EmscriptenTestCases\ProxiedExternFunction.cs" />
9697
<None Include="EmscriptenTestCases\IntPtrZeroEquality.cs" />
9798
<None Include="EmscriptenTestCases\StringArrayParameter.cs" />
@@ -381,6 +382,7 @@
381382
<None Include="UnsafeTestCases\CastAddressOfValueToArray.cs" />
382383
<None Include="UnsafeTestCases\IgnorePointerFields.cs" />
383384
<None Include="UnsafeTestCases\PointerWriteConversion_Issue686.cs" />
385+
<None Include="UnsafeTestCases\ModifyPackedStructArrayElementStructField.cs" />
384386
<Compile Include="XMLTests.cs" />
385387
<Compile Include="DependencyTests.cs" />
386388
<None Include="TestCases\GenericParameterNameShadowing.cs" />
@@ -599,4 +601,4 @@
599601
<Content Include="BinaryTestCases\InheritedAndExplicitOverwrittenMethods.dll" />
600602
<Content Include="BinaryTestCases\InheritedInterfacesExplicit.dll" />
601603
</Files>
602-
</Project>
604+
</Project>

Compiler/Profiles/ResourceConverter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,13 +197,14 @@ public static void ExtractFromAssembly (Configuration configuration, string asse
197197
);
198198

199199
foreach (var kvp in manifestResources) {
200-
Console.WriteLine(kvp.Key);
201200
var key = kvp.Key;
202201

203202
if (result.Files.ContainsKey(key)) {
204203
if (result.Files[key].Size != kvp.Value.Length)
205204
throw new InvalidOperationException("Found two conflicting manifest resources named '" + key + "'");
206205
} else {
206+
Console.WriteLine(key);
207+
207208
result.AddFile(
208209
"ManifestResource", key,
209210
new ArraySegment<byte>(kvp.Value),

JSIL/AssemblyTranslator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3051,9 +3051,11 @@ protected void DefineMethod (
30513051

30523052
TranslateOverrides(context, methodInfo.DeclaringType, method, methodInfo, astEmitter, output);
30533053

3054-
TranslateCustomAttributes(context, method.DeclaringType, method, astEmitter, output);
3054+
if (!makeSkeleton)
3055+
TranslateCustomAttributes(context, method.DeclaringType, method, astEmitter, output);
30553056

3056-
TranslateParameterAttributes(context, method.DeclaringType, method, astEmitter, output);
3057+
if (!makeSkeleton)
3058+
TranslateParameterAttributes(context, method.DeclaringType, method, astEmitter, output);
30573059

30583060
output.Semicolon();
30593061
} finally {

JSIL/FunctionCache.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ private static bool TryAcquireStaticAnalysisDataLock (Entry entry, QualifiedMemb
183183
return true;
184184
}
185185

186-
public FunctionAnalysis1stPass GetFirstPass (QualifiedMemberIdentifier method, QualifiedMemberIdentifier forMethod) {
186+
public FunctionAnalysis1stPass GetFirstPass (QualifiedMemberIdentifier method, QualifiedMemberIdentifier forCaller) {
187187
var entry = GetCacheEntry(method, false);
188188

189189
if ((entry == null) || (entry.Expression == null))
@@ -217,7 +217,11 @@ private FunctionAnalysis2ndPass _GetOrCreateSecondPass (Entry entry) {
217217
(entry.SecondPass == null) &&
218218
(entry.Expression != null)
219219
) {
220-
if (entry.Definition.IsAbstract)
220+
if (
221+
entry.Definition.IsAbstract ||
222+
// HACK: Fixes ExpressionsExecution and ExpressionsTest???
223+
(entry.FirstPass == null)
224+
)
221225
entry.SecondPass = CreateSecondPassForAbstractMethod(entry.Info);
222226
else if (entry.Definition.IsVirtual)
223227
entry.SecondPass = CreateSecondPassForOverridableMethod(entry.Info, entry.FirstPass);
@@ -228,7 +232,7 @@ private FunctionAnalysis2ndPass _GetOrCreateSecondPass (Entry entry) {
228232
return entry.SecondPass;
229233
}
230234

231-
public FunctionAnalysis2ndPass GetSecondPass (JSMethod method, QualifiedMemberIdentifier forMethod) {
235+
public FunctionAnalysis2ndPass GetSecondPass (JSMethod method, QualifiedMemberIdentifier forCaller) {
232236
if (method == null)
233237
return null;
234238

@@ -240,7 +244,7 @@ public FunctionAnalysis2ndPass GetSecondPass (JSMethod method, QualifiedMemberId
240244
if (entry == null)
241245
return null;
242246

243-
GetFirstPass(id, forMethod);
247+
GetFirstPass(id, forCaller);
244248

245249
if (!TryAcquireStaticAnalysisDataLock(entry, method.QualifiedIdentifier))
246250
return null;

JSIL/FunctionTransformPipeline.cs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,12 @@ public TypeInfoProvider TypeInfoProvider {
8888
private bool RunStaticAnalysisDependentTransform<T> (T visitor, Action<T> postVisitAction = null)
8989
where T : StaticAnalysisJSAstVisitor
9090
{
91-
try {
92-
visitor.Visit(Function);
93-
94-
if (postVisitAction != null)
95-
postVisitAction(visitor);
91+
visitor.Visit(Function);
9692

97-
return true;
98-
} catch (TemporarilySuspendTransformPipelineException exc) {
99-
if (Trace)
100-
Console.WriteLine(
101-
"Static analysis for '{2}::{3}' suspended due to dependency on '{0}::{1}'",
102-
exc.Identifier.Type.Name, exc.Identifier.Member.Name,
103-
Identifier.Type.Name, Identifier.Member.Name
104-
);
93+
if (postVisitAction != null)
94+
postVisitAction(visitor);
10595

106-
return false;
107-
}
96+
return true;
10897
}
10998

11099
public void Enqueue (FunctionTransformPipelineStage stage) {
@@ -131,7 +120,22 @@ public bool RunUntilCompletion () {
131120
var currentStage = Pipeline.Peek();
132121

133122
try {
134-
if (currentStage()) {
123+
var stageSucceeded = false;
124+
125+
try {
126+
stageSucceeded = currentStage();
127+
} catch (TemporarilySuspendTransformPipelineException exc) {
128+
if (Trace)
129+
Console.WriteLine(
130+
"Static analysis for '{2}::{3}' suspended due to dependency on '{0}::{1}'",
131+
exc.Identifier.Type.Name, exc.Identifier.Member.Name,
132+
Identifier.Type.Name, Identifier.Member.Name
133+
);
134+
135+
return false;
136+
}
137+
138+
if (stageSucceeded) {
135139
Pipeline.Dequeue();
136140

137141
if (CheckForStaticAnalysisChanges) {

JSIL/Transforms/StaticAnalysis/EmulateStructAssignment.cs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,17 @@ protected bool IsCopyNeeded (
176176

177177
var valueDot = value as JSDotExpressionBase;
178178

179-
// The value is being read out of an element proxy, so no copy is necessary - the read unpacks the value
180-
// on demand from the packed array.
179+
// The value is being read out of an element proxy
181180
if (
182181
(valueDot != null) &&
183182
PackedArrayUtil.IsElementProxy(valueDot.Target)
184183
) {
184+
// BROKEN: no copy is necessary - the read unpacks the value on demand from the packed array.
185185
// return false;
186+
187+
// HACK: Currently struct fields are proxies so that writes to them work correctly.
188+
// FIXME: Maybe return true here always?
189+
// return true;
186190
}
187191

188192
var valueTypeInfo = TypeInfo.GetExisting(valueType);
@@ -482,6 +486,31 @@ private bool IsVarEffectivelyConstantHere (JSVariable variable) {
482486
return false;
483487
}
484488

489+
private bool IsStructVariableNeverMutated (JSExpression expr) {
490+
var variable = expr as JSVariable;
491+
492+
if (variable == null)
493+
return false;
494+
495+
// FIXME: ref/out don't generate a SideEffect. Should they?
496+
// I think reassignment through ref/out is safe but mutation through
497+
// them is not safe. On the other hand, ref/out vars should always
498+
// generate a copy on read... right?
499+
500+
if (SecondPass == null)
501+
return false;
502+
else if (SecondPass.IndirectSideEffectVariables.Contains(variable.Name))
503+
return false;
504+
else if (SecondPass.Data == null)
505+
return false;
506+
507+
foreach (var se in SecondPass.Data.SideEffects)
508+
if (se.Variable == variable.Name)
509+
return false;
510+
511+
return true;
512+
}
513+
485514
public void VisitNode (JSBinaryOperatorExpression boe) {
486515
if (boe.Operator != JSOperator.Assignment) {
487516
base.VisitNode(boe);
@@ -498,15 +527,20 @@ public void VisitNode (JSBinaryOperatorExpression boe) {
498527
// Even if the assignment target is never modified, if the assignment *source*
499528
// gets modified, we need to make a copy here, because the target is probably
500529
// being used as a back-up copy.
501-
var rightVarsModified = (rightVars.Any((rv) => SecondPass.IsVariableModified(rv.Name)));
530+
var rightVarsMutated = rightVars.Any((rv) => !IsStructVariableNeverMutated(rv));
502531
var rightVarsAreReferences = rightVars.Any((rv) => rv.IsReference);
503532

533+
// We need to ensure that the lhs is never mutated. Reassignment is fine.
534+
bool leftVarMutated = !IsStructVariableNeverMutated(boe.Left);
535+
504536
bool rightVarIsEffectivelyConstantHere =
505-
IsVarEffectivelyConstantHere(boe.Right as JSVariable);
537+
IsVarEffectivelyConstantHere(boe.Right as JSVariable) &&
538+
!leftVarMutated;
506539

507540
if (
508541
(
509-
rightVarsModified ||
542+
rightVarsMutated ||
543+
leftVarMutated ||
510544
IsCopyNeededForAssignmentTarget(boe.Left) ||
511545
rightVarsAreReferences
512546
) &&
@@ -522,7 +556,9 @@ public void VisitNode (JSBinaryOperatorExpression boe) {
522556
if (TraceElidedCopies) {
523557
Console.WriteLine(
524558
"struct copy elided for assignment {0} = {1}{2}", boe.Left, boe.Right,
525-
rightVarIsEffectivelyConstantHere ? " (rhs is effectively constant due to following all other accesses)" : ""
559+
rightVarIsEffectivelyConstantHere
560+
? " (rhs is effectively constant due to following all other accesses)"
561+
: ""
526562
);
527563
}
528564
}

JSIL/Transforms/StaticAnalysis/StaticAnalyzer.cs

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,10 @@ public void VisitNode (JSFieldAccess fa) {
266266
parentNodeIndices, StatementIndex, NodeIndex, field.Field.DeclaringType
267267
));
268268
} else if (v != null) {
269-
State.SideEffects.Add(new FunctionAnalysis1stPass.SideEffect(
270-
parentNodeIndices, StatementIndex, NodeIndex, v.Identifier, "field modified"
271-
));
269+
if (fa.IsWrite)
270+
State.SideEffects.Add(new FunctionAnalysis1stPass.SideEffect(
271+
parentNodeIndices, StatementIndex, NodeIndex, v.Identifier, "field modified"
272+
));
272273
}
273274

274275
bool isRead = true;
@@ -403,7 +404,7 @@ public void VisitNode (JSInvocationExpression ie) {
403404
));
404405

405406
// HACK: Synthesize an assignment record for direct invocations of constructors on struct locals
406-
if ((method) != null && (method.Method.Name == ".ctor")) {
407+
if ((method != null) && (method.Method.Name == ".ctor")) {
407408
var t = thisVar.GetActualType(TypeSystem);
408409
var synthesizedAssignment = new FunctionAnalysis1stPass.Assignment(
409410
pni, StatementIndex, NodeIndex, thisVar.Name,
@@ -749,6 +750,7 @@ public class FunctionAnalysis2ndPass {
749750
public readonly HashSet<HashSet<FieldInfo>> RecursivelyMutatedFields;
750751
private readonly HashSet<string> ModifiedVariables;
751752
private readonly HashSet<string> EscapingVariables;
753+
public readonly HashSet<string> IndirectSideEffectVariables;
752754
public readonly string ResultVariable;
753755
public readonly bool ResultIsNew;
754756
public readonly bool ViolatesThisReferenceImmutability;
@@ -768,6 +770,15 @@ bool isSealed
768770
Data = data;
769771
IsSealed = isSealed;
770772

773+
if (data == null)
774+
throw new ArgumentNullException("data");
775+
else if (data.Function == null)
776+
throw new ArgumentNullException("data.Function");
777+
else if (data.Function.Method == null)
778+
throw new ArgumentNullException("data.Function.Method");
779+
else if (data.Function.Method.Method == null)
780+
throw new ArgumentNullException("data.Function.Method.Method");
781+
771782
if (data.Function.Method.Method.Metadata.HasAttribute("JSIsPure"))
772783
_IsPure = true;
773784
else
@@ -940,14 +951,60 @@ from p in data.Function.Parameters select p.Name
940951
}
941952

942953
if (
943-
!data.Function.Method.Method.IsStatic &&
944-
data.Function.Method.Method.DeclaringType.IsImmutable &&
945-
data.ReassignsThisReference
954+
(
955+
!data.Function.Method.Method.IsStatic &&
956+
data.Function.Method.Method.DeclaringType.IsImmutable &&
957+
data.ReassignsThisReference
958+
) ||
959+
data.Function.Method.Method.Name == ".ctor"
946960
) {
947961
ViolatesThisReferenceImmutability = true;
948962
ModifiedVariables.Add("this");
949963
}
950964

965+
IndirectSideEffectVariables = new HashSet<string>();
966+
967+
// Invocations that reassign or mutate this need to have a synthesized side effect
968+
foreach (var invocation in data.Invocations) {
969+
if (invocation.ThisVariable == null)
970+
continue;
971+
else if (IndirectSideEffectVariables.Contains(invocation.ThisVariable))
972+
continue;
973+
974+
bool shouldSynthesizeSideEffect = false;
975+
do {
976+
var jsm = invocation.Method;
977+
if (jsm == null) {
978+
shouldSynthesizeSideEffect = true;
979+
break;
980+
}
981+
982+
var targetSecondPass = functionCache.GetSecondPass(invocation.Method, data.Identifier);
983+
if (targetSecondPass == null) {
984+
shouldSynthesizeSideEffect = true;
985+
break;
986+
}
987+
988+
if (targetSecondPass.ViolatesThisReferenceImmutability) {
989+
shouldSynthesizeSideEffect = true;
990+
break;
991+
} else if (
992+
(targetSecondPass.Data != null) &&
993+
targetSecondPass.Data.SideEffects.Any(se => se.Variable == "this")
994+
) {
995+
// FIXME: Is this necessary or is it overly conservative?
996+
shouldSynthesizeSideEffect = true;
997+
break;
998+
}
999+
1000+
if (!shouldSynthesizeSideEffect)
1001+
;
1002+
} while (false);
1003+
1004+
if (shouldSynthesizeSideEffect)
1005+
IndirectSideEffectVariables.Add(invocation.ThisVariable);
1006+
}
1007+
9511008
MutatedFields = new HashSet<FieldInfo>(
9521009
from fa in data.FieldAccesses where !fa.IsRead select fa.Field.Field
9531010
);
@@ -1006,12 +1063,15 @@ public FunctionAnalysis2ndPass (FunctionCache functionCache, MethodInfo method)
10061063
}
10071064

10081065
VariableAliases = new Dictionary<string, HashSet<string>>();
1066+
IndirectSideEffectVariables = new HashSet<string>();
10091067

10101068
ResultVariable = null;
10111069
ResultIsNew = method.Metadata.HasAttribute("JSIL.Meta.JSResultIsNew");
10121070

10131071
MutatedFields = null;
10141072

1073+
ViolatesThisReferenceImmutability = (method.Name == ".ctor");
1074+
10151075
Trace(method.Member.FullName);
10161076
}
10171077

LICENSE

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2011-2015 Katelyn Gadd
4+
Some sponsored contributions (c) Mozilla Corporation
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

Libraries/JSIL.Bootstrap.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4545,6 +4545,9 @@ $jsilcore.GetSerializationScratchBuffers = function () {
45454545
offset = offset | 0;
45464546
count = count | 0;
45474547

4548+
if (!bytes)
4549+
JSIL.RuntimeError("bytes cannot be null");
4550+
45484551
for (var i = 0; i < count; i++)
45494552
this.uint8[i] = bytes[offset + i];
45504553
}

0 commit comments

Comments
 (0)