@@ -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+ */
14691471static 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
17071489static 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+ */
20561843static 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