Skip to content

Commit 9f72590

Browse files
committed
The value of a read-only field in an embedded object or type can no longer be changed
1 parent 3aad478 commit 9f72590

File tree

9 files changed

+209
-114
lines changed

9 files changed

+209
-114
lines changed

src/MsieJavaScriptEngine/ActiveScript/HostItemBase.cs

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -190,22 +190,17 @@ private static MethodInfo[] GetAvailableMethods(MethodInfo[] methods, bool allow
190190
return availableMethods;
191191
}
192192

193-
private bool IsField(string name)
193+
private FieldInfo GetField(string name)
194194
{
195-
bool isField = false;
196-
FieldInfo[] fields = _fields;
197-
int fieldCount = fields.Length;
198-
199-
for (int fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++)
195+
foreach (FieldInfo field in _fields)
200196
{
201-
if (fields[fieldIndex].Name.Equals(name, StringComparison.Ordinal))
197+
if (field.Name.Equals(name, StringComparison.Ordinal))
202198
{
203-
isField = true;
204-
break;
199+
return field;
205200
}
206201
}
207202

208-
return isField;
203+
return null;
209204
}
210205

211206
protected abstract object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
@@ -215,25 +210,34 @@ protected object InvokeStandardMember(string name, BindingFlags invokeAttr, Bind
215210
object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
216211
{
217212
BindingFlags processedInvokeAttr = invokeAttr;
218-
if ((processedInvokeAttr.HasFlag(BindingFlags.GetProperty)
213+
if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty)
219214
|| processedInvokeAttr.HasFlag(BindingFlags.SetProperty)
220215
|| processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
221-
&& IsField(name))
222216
{
223-
if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty))
224-
{
225-
processedInvokeAttr &= ~BindingFlags.GetProperty;
226-
processedInvokeAttr |= BindingFlags.GetField;
227-
}
228-
else if (processedInvokeAttr.HasFlag(BindingFlags.SetProperty))
217+
FieldInfo field = GetField(name);
218+
if (field is not null)
229219
{
230-
processedInvokeAttr &= ~BindingFlags.SetProperty;
231-
processedInvokeAttr |= BindingFlags.SetField;
232-
}
233-
else if (processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
234-
{
235-
processedInvokeAttr &= ~BindingFlags.PutDispProperty;
236-
processedInvokeAttr |= BindingFlags.SetField;
220+
if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty))
221+
{
222+
processedInvokeAttr &= ~BindingFlags.GetProperty;
223+
processedInvokeAttr |= BindingFlags.GetField;
224+
}
225+
else if (processedInvokeAttr.HasFlag(BindingFlags.SetProperty))
226+
{
227+
processedInvokeAttr &= ~BindingFlags.SetProperty;
228+
processedInvokeAttr |= BindingFlags.SetField;
229+
}
230+
else if (processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty))
231+
{
232+
if (field.IsInitOnly)
233+
{
234+
// Prevents a setting of value to the read-only field
235+
return null;
236+
}
237+
238+
processedInvokeAttr &= ~BindingFlags.PutDispProperty;
239+
processedInvokeAttr |= BindingFlags.SetField;
240+
}
237241
}
238242
}
239243

src/MsieJavaScriptEngine/JsRt/Edge/EdgeTypeMapper.cs

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -369,56 +369,59 @@ private void ProjectFields(EdgeEmbeddedItem externalItem)
369369
EdgeJsValue getMethodValue = EdgeJsValue.CreateFunction(nativeGetFunction);
370370
descriptorValue.SetProperty("get", getMethodValue, true);
371371

372-
EdgeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
372+
if (!field.IsInitOnly)
373373
{
374-
EdgeJsValue undefinedValue = EdgeJsValue.Undefined;
375-
376-
if (instance && obj is null)
374+
EdgeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
377375
{
378-
CreateAndSetTypeError(string.Format(
379-
CommonStrings.Runtime_InvalidThisContextForHostObjectField, fieldName));
380-
return undefinedValue;
381-
}
376+
EdgeJsValue undefinedValue = EdgeJsValue.Undefined;
382377

383-
object value = MapToHostType(args[1]);
384-
ReflectionHelpers.FixFieldValueType(ref value, field);
378+
if (instance && obj is null)
379+
{
380+
CreateAndSetTypeError(string.Format(
381+
CommonStrings.Runtime_InvalidThisContextForHostObjectField, fieldName));
382+
return undefinedValue;
383+
}
385384

386-
try
387-
{
388-
field.SetValue(obj, value);
389-
}
390-
catch (Exception e)
391-
{
392-
Exception exception = UnwrapException(e);
393-
var wrapperException = exception as WrapperException;
394-
EdgeJsValue errorValue;
385+
object value = MapToHostType(args[1]);
386+
ReflectionHelpers.FixFieldValueType(ref value, field);
395387

396-
if (wrapperException is not null)
388+
try
397389
{
398-
errorValue = CreateErrorFromWrapperException(wrapperException);
390+
field.SetValue(obj, value);
399391
}
400-
else
392+
catch (Exception e)
401393
{
402-
string errorMessage = instance ?
403-
string.Format(CommonStrings.Runtime_HostObjectFieldSettingFailed, fieldName,
404-
exception.Message)
405-
:
406-
string.Format(CommonStrings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName,
407-
exception.Message)
408-
;
409-
errorValue = EdgeJsErrorHelpers.CreateError(errorMessage);
394+
Exception exception = UnwrapException(e);
395+
var wrapperException = exception as WrapperException;
396+
EdgeJsValue errorValue;
397+
398+
if (wrapperException is not null)
399+
{
400+
errorValue = CreateErrorFromWrapperException(wrapperException);
401+
}
402+
else
403+
{
404+
string errorMessage = instance ?
405+
string.Format(CommonStrings.Runtime_HostObjectFieldSettingFailed, fieldName,
406+
exception.Message)
407+
:
408+
string.Format(CommonStrings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName,
409+
exception.Message)
410+
;
411+
errorValue = EdgeJsErrorHelpers.CreateError(errorMessage);
412+
}
413+
EdgeJsContext.SetException(errorValue);
414+
415+
return undefinedValue;
410416
}
411-
EdgeJsContext.SetException(errorValue);
412417

413418
return undefinedValue;
414-
}
415-
416-
return undefinedValue;
417-
};
418-
nativeFunctions.Add(nativeSetFunction);
419+
};
420+
nativeFunctions.Add(nativeSetFunction);
419421

420-
EdgeJsValue setMethodValue = EdgeJsValue.CreateFunction(nativeSetFunction);
421-
descriptorValue.SetProperty("set", setMethodValue, true);
422+
EdgeJsValue setMethodValue = EdgeJsValue.CreateFunction(nativeSetFunction);
423+
descriptorValue.SetProperty("set", setMethodValue, true);
424+
}
422425

423426
typeValue.DefineProperty(fieldName, descriptorValue);
424427
}
@@ -448,7 +451,7 @@ private void ProjectProperties(EdgeEmbeddedItem externalItem)
448451
EdgeJsValue descriptorValue = EdgeJsValue.CreateObject();
449452
descriptorValue.SetProperty("enumerable", EdgeJsValue.True, true);
450453

451-
if (property.GetGetMethod() is not null)
454+
if (property.CanRead)
452455
{
453456
EdgeJsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
454457
{
@@ -503,7 +506,7 @@ private void ProjectProperties(EdgeEmbeddedItem externalItem)
503506
descriptorValue.SetProperty("get", getMethodValue, true);
504507
}
505508

506-
if (property.GetSetMethod() is not null)
509+
if (property.CanWrite)
507510
{
508511
EdgeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
509512
{

src/MsieJavaScriptEngine/JsRt/Ie/IeTypeMapper.cs

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -369,56 +369,59 @@ private void ProjectFields(IeEmbeddedItem externalItem)
369369
IeJsValue getMethodValue = IeJsValue.CreateFunction(nativeGetFunction);
370370
descriptorValue.SetProperty("get", getMethodValue, true);
371371

372-
IeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
372+
if (!field.IsInitOnly)
373373
{
374-
IeJsValue undefinedValue = IeJsValue.Undefined;
375-
376-
if (instance && obj is null)
374+
IeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
377375
{
378-
CreateAndSetTypeError(string.Format(
379-
CommonStrings.Runtime_InvalidThisContextForHostObjectField, fieldName));
380-
return undefinedValue;
381-
}
376+
IeJsValue undefinedValue = IeJsValue.Undefined;
382377

383-
object value = MapToHostType(args[1]);
384-
ReflectionHelpers.FixFieldValueType(ref value, field);
378+
if (instance && obj is null)
379+
{
380+
CreateAndSetTypeError(string.Format(
381+
CommonStrings.Runtime_InvalidThisContextForHostObjectField, fieldName));
382+
return undefinedValue;
383+
}
385384

386-
try
387-
{
388-
field.SetValue(obj, value);
389-
}
390-
catch (Exception e)
391-
{
392-
Exception exception = UnwrapException(e);
393-
var wrapperException = exception as WrapperException;
394-
IeJsValue errorValue;
385+
object value = MapToHostType(args[1]);
386+
ReflectionHelpers.FixFieldValueType(ref value, field);
395387

396-
if (wrapperException is not null)
388+
try
397389
{
398-
errorValue = CreateErrorFromWrapperException(wrapperException);
390+
field.SetValue(obj, value);
399391
}
400-
else
392+
catch (Exception e)
401393
{
402-
string errorMessage = instance ?
403-
string.Format(CommonStrings.Runtime_HostObjectFieldSettingFailed, fieldName,
404-
exception.Message)
405-
:
406-
string.Format(CommonStrings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName,
407-
exception.Message)
408-
;
409-
errorValue = IeJsErrorHelpers.CreateError(errorMessage);
394+
Exception exception = UnwrapException(e);
395+
var wrapperException = exception as WrapperException;
396+
IeJsValue errorValue;
397+
398+
if (wrapperException is not null)
399+
{
400+
errorValue = CreateErrorFromWrapperException(wrapperException);
401+
}
402+
else
403+
{
404+
string errorMessage = instance ?
405+
string.Format(CommonStrings.Runtime_HostObjectFieldSettingFailed, fieldName,
406+
exception.Message)
407+
:
408+
string.Format(CommonStrings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName,
409+
exception.Message)
410+
;
411+
errorValue = IeJsErrorHelpers.CreateError(errorMessage);
412+
}
413+
IeJsContext.SetException(errorValue);
414+
415+
return undefinedValue;
410416
}
411-
IeJsContext.SetException(errorValue);
412417

413418
return undefinedValue;
414-
}
415-
416-
return undefinedValue;
417-
};
418-
nativeFunctions.Add(nativeSetFunction);
419+
};
420+
nativeFunctions.Add(nativeSetFunction);
419421

420-
IeJsValue setMethodValue = IeJsValue.CreateFunction(nativeSetFunction);
421-
descriptorValue.SetProperty("set", setMethodValue, true);
422+
IeJsValue setMethodValue = IeJsValue.CreateFunction(nativeSetFunction);
423+
descriptorValue.SetProperty("set", setMethodValue, true);
424+
}
422425

423426
typeValue.DefineProperty(fieldName, descriptorValue);
424427
}
@@ -448,7 +451,7 @@ private void ProjectProperties(IeEmbeddedItem externalItem)
448451
IeJsValue descriptorValue = IeJsValue.CreateObject();
449452
descriptorValue.SetProperty("enumerable", IeJsValue.True, true);
450453

451-
if (property.GetGetMethod() is not null)
454+
if (property.CanRead)
452455
{
453456
IeJsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
454457
{
@@ -503,7 +506,7 @@ private void ProjectProperties(IeEmbeddedItem externalItem)
503506
descriptorValue.SetProperty("get", getMethodValue, true);
504507
}
505508

506-
if (property.GetSetMethod() is not null)
509+
if (property.CanWrite)
507510
{
508511
IeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) =>
509512
{

src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
2. Added support for .NET Standard 2.1 and .NET 10;
2929
3. Performed a migration to the modern C# null/not-null checks;
3030
4. In the `lock` statements for .NET 10 target now uses a instances of the `System.Threading.Lock` class;
31-
5. Reduced a memory allocation by using collection expressions.</PackageReleaseNotes>
31+
5. Reduced a memory allocation by using collection expressions;
32+
6. The value of a read-only field in an embedded object or type can no longer be changed.</PackageReleaseNotes>
3233
<NeutralLanguage>en-US</NeutralLanguage>
3334
<PackageOutputPath>../../nuget</PackageOutputPath>
3435
<GeneratePackageOnBuild Condition=" '$(Configuration)' == 'Release' ">true</GeneratePackageOnBuild>

src/MsieJavaScriptEngine/readme.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
3. Performed a migration to the modern C# null/not-null checks;
2727
4. In the `lock` statements for .NET 10 target now uses a instances of the
2828
`System.Threading.Lock` class;
29-
5. Reduced a memory allocation by using collection expressions.
29+
5. Reduced a memory allocation by using collection expressions;
30+
6. The value of a read-only field in an embedded object or type can no longer be
31+
changed.
3032

3133
============
3234
PROJECT SITE

test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Threading.Tasks;
6-
7-
namespace MsieJavaScriptEngine.Benchmarks.Interop.ObjectsEmbedding
1+
namespace MsieJavaScriptEngine.Benchmarks.Interop.ObjectsEmbedding
82
{
93
public abstract class SomeClassBase
104
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
namespace MsieJavaScriptEngine.Test.Common.Interop
4+
{
5+
public struct Age
6+
{
7+
public readonly int Year;
8+
9+
10+
public Age(int year)
11+
{
12+
Year = year;
13+
}
14+
15+
16+
public override string ToString()
17+
{
18+
int age = DateTime.Now.Year - Year;
19+
20+
return age.ToString();
21+
}
22+
}
23+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace MsieJavaScriptEngine.Test.Common.Interop
2+
{
3+
public static class RuntimeConstants
4+
{
5+
public static readonly int MinValue = 0;
6+
public static readonly int MaxValue = 999;
7+
}
8+
}

0 commit comments

Comments
 (0)