Skip to content

Commit cc4db9b

Browse files
authored
[hot_reload] Remove pass1 (#95130)
* [hot_reload] Remove pass1 The job of pass1 was to validate that the EnC delta had the expected structure. This was particularly useful before Roslyn added the capabilities API and it could potentially send us changes that we weren't able to handle yet. In some cases we were actually too strict and disallowed benign edits (that sometimes happen because it is easier for roslyn to send an update that has no effect than to compute how to leave it out). But now Mono is in lock-step with all supported edits, so pass1 is not necessary. Pass2 still has some structural checks to validate some of our assumptions, but we don't expect them to be violated in any supported edits. Fixes #90153
1 parent 5c90e68 commit cc4db9b

1 file changed

Lines changed: 32 additions & 253 deletions

File tree

src/mono/mono/component/hot_reload.c

Lines changed: 32 additions & 253 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,245 +1463,27 @@ prepare_mutated_rows (const MonoTableInfo *table_enclog, MonoImage *image_base,
14631463
}
14641464
}
14651465

1466-
/* Run some sanity checks first. If we detect unsupported scenarios, this
1467-
* function will fail and the metadata update should be aborted. This should
1468-
* run before anything in the metadata world is updated. */
1466+
/*
1467+
* Returns TRUE if modifications or additions to the table imply that the execution engine should
1468+
* invalidate existing methods. It's better to be pessimistic (say TRUE more often) - most EnC
1469+
* deltas will lead to a change in behavior.
1470+
*/
14691471
static gboolean
1470-
apply_enclog_pass1 (MonoImage *image_base, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, gboolean *should_invalidate_transformed_code, MonoError *error)
1472+
table_should_invalidate_transformed_code (int token_table)
14711473
{
1472-
*should_invalidate_transformed_code = false;
1473-
MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG];
1474-
guint32 rows = table_info_get_rows (table_enclog);
1475-
1476-
gboolean unsupported_edits = FALSE;
1477-
1478-
/* hack: make a pass over it, looking only for table method updates, in
1479-
* order to give more meaningful error messages first */
1480-
1481-
for (guint32 i = 0; i < rows ; ++i) {
1482-
guint32 cols [MONO_ENCLOG_SIZE];
1483-
mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE);
1484-
1485-
// FIXME: the top bit 0x8000000 of log_token is some kind of
1486-
// indicator see IsRecId in metamodelrw.cpp and
1487-
// MDInternalRW::EnumDeltaTokensInit which skips over those
1488-
// records when EditAndContinueModule::ApplyEditAndContinue is
1489-
// iterating.
1490-
int log_token = cols [MONO_ENCLOG_TOKEN];
1491-
int func_code = cols [MONO_ENCLOG_FUNC_CODE];
1492-
1493-
int token_table = mono_metadata_token_table (log_token);
1494-
guint32 token_index = mono_metadata_token_index (log_token);
1495-
1496-
gboolean is_addition = token_index-1 >= delta_info->count[token_table].prev_gen_rows ;
1497-
1498-
1499-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x (%s idx=0x%02x) (base table has 0x%04x rows; prev gen had 0x%04x rows)\t%s\tfunc=0x%02x (\"%s\")\n", i, log_token, mono_meta_table_name (token_table), token_index, table_info_get_rows (&image_base->tables [token_table]), delta_info->count[token_table].prev_gen_rows, (is_addition ? "ADD" : "UPDATE"), func_code, funccode_to_str (func_code));
1500-
1501-
1502-
if (token_table != MONO_TABLE_METHOD)
1503-
continue;
1504-
1505-
/* adding a new parameter to a new method is ok */
1506-
if (func_code == ENC_FUNC_ADD_PARAM && is_addition)
1507-
continue;
1508-
1509-
g_assert (func_code == 0); /* anything else doesn't make sense here */
1510-
}
1511-
1512-
for (guint32 i = 0; i < rows ; ++i) {
1513-
guint32 cols [MONO_ENCLOG_SIZE];
1514-
mono_metadata_decode_row (table_enclog, i, cols, MONO_ENCLOG_SIZE);
1515-
1516-
int log_token = cols [MONO_ENCLOG_TOKEN];
1517-
int func_code = cols [MONO_ENCLOG_FUNC_CODE];
1518-
1519-
int token_table = mono_metadata_token_table (log_token);
1520-
guint32 token_index = mono_metadata_token_index (log_token);
1521-
1522-
gboolean is_addition = token_index-1 >= delta_info->count[token_table].prev_gen_rows ;
1523-
1524-
switch (token_table) {
1525-
case MONO_TABLE_ASSEMBLYREF:
1526-
/* okay, supported */
1527-
break;
1528-
case MONO_TABLE_METHOD:
1529-
*should_invalidate_transformed_code = true;
1530-
if (func_code == ENC_FUNC_ADD_PARAM)
1531-
continue; /* ok, allowed */
1532-
/* handled above */
1533-
break;
1534-
case MONO_TABLE_FIELD:
1535-
/* ok */
1536-
g_assert (func_code == ENC_FUNC_DEFAULT);
1537-
break;
1538-
case MONO_TABLE_PROPERTYMAP: {
1539-
*should_invalidate_transformed_code = true;
1540-
if (func_code == ENC_FUNC_ADD_PROPERTY) {
1541-
g_assert (i + 1 < rows);
1542-
i++; /* skip the next record */
1543-
continue;
1544-
}
1545-
if (!is_addition) {
1546-
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support patching of existing table cols.", i, log_token);
1547-
mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token);
1548-
unsupported_edits = TRUE;
1549-
continue;
1550-
}
1551-
/* new rows, ok */
1552-
break;
1553-
}
1554-
case MONO_TABLE_PROPERTY: {
1555-
/* ok */
1556-
*should_invalidate_transformed_code = true;
1557-
g_assert (func_code == ENC_FUNC_DEFAULT);
1558-
break;
1559-
}
1560-
case MONO_TABLE_EVENTMAP: {
1561-
*should_invalidate_transformed_code = true;
1562-
if (func_code == ENC_FUNC_ADD_EVENT) {
1563-
g_assert (i + 1 < rows);
1564-
i++; /* skip the next record */
1565-
continue;
1566-
}
1567-
if (!is_addition) {
1568-
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support patching of existing table cols.", i, log_token);
1569-
mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token);
1570-
unsupported_edits = TRUE;
1571-
continue;
1572-
}
1573-
/* new rows, ok */
1574-
break;
1575-
}
1576-
case MONO_TABLE_METHODSEMANTICS: {
1577-
*should_invalidate_transformed_code = true;
1578-
if (is_addition) {
1579-
/* new rows are fine */
1580-
break;
1581-
} else {
1582-
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support patching of existing table cols.", i, log_token);
1583-
mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token);
1584-
unsupported_edits = TRUE;
1585-
continue;
1586-
}
1587-
}
1588-
case MONO_TABLE_CUSTOMATTRIBUTE: {
1589-
*should_invalidate_transformed_code = true;
1590-
if (!is_addition) {
1591-
/* modifying existing rows is ok, as long as the parent and ctor are the same */
1592-
guint32 ca_upd_cols [MONO_CUSTOM_ATTR_SIZE];
1593-
guint32 ca_base_cols [MONO_CUSTOM_ATTR_SIZE];
1594-
int mapped_token = hot_reload_relative_delta_index (image_dmeta, delta_info, mono_metadata_make_token (token_table, token_index));
1595-
g_assert (mapped_token != -1);
1596-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. mapped index = 0x%08x\n", i, log_token, mapped_token);
1597-
1598-
mono_metadata_decode_row (&image_dmeta->tables [MONO_TABLE_CUSTOMATTRIBUTE], mapped_token - 1, ca_upd_cols, MONO_CUSTOM_ATTR_SIZE);
1599-
mono_metadata_decode_row (&image_base->tables [MONO_TABLE_CUSTOMATTRIBUTE], token_index - 1, ca_base_cols, MONO_CUSTOM_ATTR_SIZE);
1600-
1601-
/* compare the ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] to ca_base_cols [MONO_CUSTOM_ATTR_PARENT]. */
1602-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. Old Parent 0x%08x New Parent 0x%08x\n", i, log_token, ca_base_cols [MONO_CUSTOM_ATTR_PARENT], ca_upd_cols [MONO_CUSTOM_ATTR_PARENT]);
1603-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x CUSTOM_ATTR update. Old ctor 0x%08x New ctor 0x%08x\n", i, log_token, ca_base_cols [MONO_CUSTOM_ATTR_TYPE], ca_upd_cols [MONO_CUSTOM_ATTR_TYPE]);
1604-
1605-
/* TODO: when we support the ChangeCustomAttribute capability, the
1606-
* parent might become 0 to delete attributes. It may also be the
1607-
* case that the MONO_CUSTOM_ATTR_TYPE will change. Without that
1608-
* capability, we trust that if the TYPE is not the same token, it
1609-
* still resolves to the same MonoMethod* (but we can't check it in
1610-
* pass1 because we haven't added the new AssemblyRefs yet.
1611-
*/
1612-
/* NOTE: Apparently Roslyn sometimes sends NullableContextAttribute
1613-
* deletions even if the ChangeCustomAttribute capability is unset.
1614-
* So tacitly accept updates where a custom attribute is deleted
1615-
* (its parent is set to 0). Once we support custom attribute
1616-
* changes, we will support this kind of deletion for real.
1617-
*/
1618-
/* FIXME: https://github.com/dotnet/roslyn/issues/60125
1619-
* Roslyn seems to emit CA rows out of order which puts rows with unexpected Parent values in the wrong place.
1620-
*/
1621-
#ifdef DISALLOW_BROKEN_PARENT
1622-
if (ca_base_cols [MONO_CUSTOM_ATTR_PARENT] != ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] && ca_upd_cols [MONO_CUSTOM_ATTR_PARENT] != 0) {
1623-
mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing CA table cols with a different Parent. token=0x%08x", log_token);
1624-
unsupported_edits = TRUE;
1625-
continue;
1626-
}
1627-
#endif
1628-
break;
1629-
} else {
1630-
/* Added a row. ok */
1631-
break;
1632-
}
1633-
}
1634-
case MONO_TABLE_PARAM: {
1635-
*should_invalidate_transformed_code = true;
1636-
break;
1637-
}
1638-
case MONO_TABLE_TYPEDEF: {
1639-
*should_invalidate_transformed_code = true;
1640-
if (func_code == ENC_FUNC_ADD_METHOD) {
1641-
/* next record should be a MONO_TABLE_METHOD addition (func == default) */
1642-
g_assert (i + 1 < rows);
1643-
guint32 next_cols [MONO_ENCLOG_SIZE];
1644-
mono_metadata_decode_row (table_enclog, i + 1, next_cols, MONO_ENCLOG_SIZE);
1645-
g_assert (next_cols [MONO_ENCLOG_FUNC_CODE] == ENC_FUNC_DEFAULT);
1646-
int next_token = next_cols [MONO_ENCLOG_TOKEN];
1647-
int next_table = mono_metadata_token_table (next_token);
1648-
guint32 next_index = mono_metadata_token_index (next_token);
1649-
g_assert (next_table == MONO_TABLE_METHOD);
1650-
/* expecting an added method */
1651-
g_assert (next_index-1 >= delta_info->count[next_table].prev_gen_rows);
1652-
i++; /* skip the next record */
1653-
continue;
1654-
}
1655-
1656-
if (func_code == ENC_FUNC_ADD_FIELD) {
1657-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x AddField to klass 0x%08x, skipping next EnClog record", i, log_token, token_index);
1658-
g_assert (i + 1 < rows);
1659-
guint32 next_cols [MONO_ENCLOG_SIZE];
1660-
mono_metadata_decode_row (table_enclog, i + 1, next_cols, MONO_ENCLOG_SIZE);
1661-
g_assert (next_cols [MONO_ENCLOG_FUNC_CODE] == ENC_FUNC_DEFAULT);
1662-
int next_token = next_cols [MONO_ENCLOG_TOKEN];
1663-
int next_table = mono_metadata_token_table (next_token);
1664-
guint32 next_index = mono_metadata_token_index (next_token);
1665-
g_assert (next_table == MONO_TABLE_FIELD);
1666-
/* expecting an added field */
1667-
g_assert (next_index-1 >= delta_info->count[next_table].prev_gen_rows);
1668-
i++; /* skip the next record */
1669-
continue;
1670-
}
1671-
// don't expect to see any other func codes with this table
1672-
g_assert (func_code == ENC_FUNC_DEFAULT);
1673-
// If it's an addition, it's an added type definition, continue.
1674-
1675-
// If it's a modification, Roslyn sometimes sends this when a custom
1676-
// attribute is deleted from a type definition.
1677-
break;
1678-
}
1679-
default:
1680-
if (!is_addition) {
1681-
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x we do not support patching of existing table cols.", i, log_token);
1682-
mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: we do not support patching of existing table cols. token=0x%08x", log_token);
1683-
unsupported_edits = TRUE;
1684-
continue;
1685-
}
1686-
}
1687-
1688-
1689-
/*
1690-
* So the way a non-default func_code works is that it's attached to the EnCLog
1691-
* record preceding the new member definition (so e.g. an addMethod code will be on
1692-
* the preceding MONO_TABLE_TYPEDEF enc record that identifies the parent type).
1693-
*/
1694-
switch (func_code) {
1695-
case ENC_FUNC_DEFAULT: /* default */
1696-
break;
1697-
default:
1698-
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "row[0x%02x]:0x%08x FunCode %d (%s) not supported (token=0x%08x)", i, log_token, func_code, funccode_to_str (func_code), log_token);
1699-
mono_error_set_type_load_name (error, NULL, image_base->name, "EnC: FuncCode %d (%s) not supported (token=0x%08x)", func_code, funccode_to_str (func_code), log_token);
1700-
unsupported_edits = TRUE;
1701-
continue;
1702-
}
1474+
switch (token_table) {
1475+
case MONO_TABLE_METHOD:
1476+
case MONO_TABLE_PROPERTYMAP:
1477+
case MONO_TABLE_PROPERTY:
1478+
case MONO_TABLE_EVENTMAP:
1479+
case MONO_TABLE_METHODSEMANTICS:
1480+
case MONO_TABLE_CUSTOMATTRIBUTE:
1481+
case MONO_TABLE_PARAM:
1482+
case MONO_TABLE_TYPEDEF:
1483+
return TRUE;
1484+
default:
1485+
return FALSE;
17031486
}
1704-
return !unsupported_edits;
17051487
}
17061488

17071489
static void
@@ -2052,9 +1834,14 @@ pass2_update_nested_classes (Pass2Context *ctx, MonoImage *image_base, MonoError
20521834

20531835
}
20541836

2055-
/* do actual enclog application */
1837+
/*
1838+
* Apply the entries from the EnC log to the runtime type system (as opposed to just mutating the
1839+
* metadata table contents). This is "pass2" because historically there was a "pass1" that just
1840+
* validated the EnC log to ensure that Mono didn't see any changes that it was not prepared to
1841+
* handle yet. Pass1 was non-destructive, while Pass2 actually makes changes that are hard to undo.
1842+
*/
20561843
static gboolean
2057-
apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, MonoError *error)
1844+
apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base_info, uint32_t generation, MonoImage *image_dmeta, DeltaInfo *delta_info, gconstpointer dil_data, uint32_t dil_length, gboolean *should_invalidate_transformed_code, MonoError *error)
20581845
{
20591846
MonoTableInfo *table_enclog = &image_dmeta->tables [MONO_TABLE_ENCLOG];
20601847
guint32 rows = table_info_get_rows (table_enclog);
@@ -2102,6 +1889,8 @@ apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base
21021889

21031890
gboolean is_addition = token_index-1 >= delta_info->count[token_table].prev_gen_rows ;
21041891

1892+
*should_invalidate_transformed_code |= table_should_invalidate_transformed_code (token_table);
1893+
21051894
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "enclog i=%d: token=0x%08x (table=%s): %d:\t%s", i, log_token, mono_meta_table_name (token_table), func_code, (is_addition ? "ADD" : "UPDATE"));
21061895

21071896

@@ -2140,7 +1929,7 @@ apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base
21401929
}
21411930

21421931
default:
2143-
g_error ("EnC: unsupported FuncCode, should be caught by pass1");
1932+
g_error ("EnC: unsupported FuncCode");
21441933
break;
21451934
}
21461935

@@ -2199,7 +1988,7 @@ apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base
21991988
delta_info->method_ppdb_table_update = g_hash_table_new (g_direct_hash, g_direct_equal);
22001989

22011990
if (is_addition) {
2202-
g_assertf (add_member_typedef, "EnC: new method added but I don't know the class, should be caught by pass1");
1991+
g_assertf (add_member_typedef, "EnC: malformed EnC delta, new method added but I don't know the class");
22031992
if (pass2_context_is_skeleton (ctx, add_member_typedef)) {
22041993
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE, "Adding new method 0x%08x to new class 0x%08x", log_token, add_member_typedef);
22051994
pass2_context_add_skeleton_member (ctx, add_member_typedef, log_token);
@@ -2438,11 +2227,9 @@ apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base
24382227
break;
24392228
}
24402229
case MONO_TABLE_CUSTOMATTRIBUTE: {
2441-
/* ok, pass1 checked for disallowed modifications */
24422230
break;
24432231
}
24442232
case MONO_TABLE_PARAM: {
2445-
/* ok, pass1 checked for disallowed modifications */
24462233
/* if there were multiple added methods, this comes in as several method
24472234
* additions, followed by the parameter additions.
24482235
*
@@ -2483,7 +2270,7 @@ apply_enclog_pass2 (Pass2Context *ctx, MonoImage *image_base, BaselineInfo *base
24832270
}
24842271
case MONO_TABLE_METHODSEMANTICS: {
24852272
g_assert (is_addition);
2486-
/* added rows ok (for new or existing classes). modifications rejected by pass1 */
2273+
/* added rows ok (for new or existing classes). don't expect modifications */
24872274
uint32_t sema_cols[MONO_METHOD_SEMA_SIZE];
24882275

24892276
uint32_t mapped_token = hot_reload_relative_delta_index (image_dmeta, delta_info, log_token);
@@ -2659,8 +2446,6 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta
26592446

26602447
/* Process EnCMap and compute number of added/modified rows from this
26612448
* delta. This enables computing row indexes relative to the delta.
2662-
* We use it in pass1 to bail out early if the EnCLog has unsupported
2663-
* edits.
26642449
*/
26652450
if (!delta_info_compute_table_records (image_dmeta, image_base, base_info, delta_info)) {
26662451
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error on computing delta table info (base=%s)", basename);
@@ -2674,13 +2459,6 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta
26742459

26752460
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Populated mutated tables for delta image %p", image_dmeta);
26762461

2677-
gboolean should_invalidate_transformed_code = false;
2678-
if (!apply_enclog_pass1 (image_base, image_dmeta, delta_info, dil_bytes, dil_length, &should_invalidate_transformed_code, error)) {
2679-
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error on sanity-checking delta image to base=%s, due to: %s", basename, mono_error_get_message (error));
2680-
hot_reload_update_cancel (generation);
2681-
return;
2682-
}
2683-
26842462
/* if there are updates, start tracking the tables of the base image, if we weren't already. */
26852463
if (table_info_get_rows (table_enclog))
26862464
table_to_image_add (image_base);
@@ -2691,9 +2469,10 @@ hot_reload_apply_changes (int origin, MonoImage *image_base, gconstpointer dmeta
26912469
if (mono_trace_is_traced (G_LOG_LEVEL_DEBUG, MONO_TRACE_METADATA_UPDATE))
26922470
dump_update_summary (image_base, image_dmeta);
26932471

2472+
gboolean should_invalidate_transformed_code = FALSE;
26942473
Pass2Context pass2ctx = {0,};
26952474
pass2_context_init (&pass2ctx);
2696-
if (!apply_enclog_pass2 (&pass2ctx, image_base, base_info, generation, image_dmeta, delta_info, dil_bytes, dil_length, error)) {
2475+
if (!apply_enclog_pass2 (&pass2ctx, image_base, base_info, generation, image_dmeta, delta_info, dil_bytes, dil_length, &should_invalidate_transformed_code, error)) {
26972476
pass2_context_destroy (&pass2ctx);
26982477
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_METADATA_UPDATE, "Error applying delta image to base=%s, due to: %s", basename, mono_error_get_message (error));
26992478
hot_reload_update_cancel (generation);

0 commit comments

Comments
 (0)