visitPropertyAccess method

  1. @override
Object? visitPropertyAccess(
  1. SPropertyAccess node
)
override

Visit a SPropertyAccess.

Implementation

@override
Object? visitPropertyAccess(SPropertyAccess node) {
  final target = node.target?.accept<Object?>(this);
  if (target is AsyncSuspensionRequest) {
    // Propagate suspension so the state machine resumes this node after resolution
    return target;
  }

  // Determine if this is a conditional access by checking the operator field
  final isNullAware = node.operator == '?.';
  final propertyName = node.propertyName!.name;

  // Null safety support: if the target is null and the access is null-aware, return null
  if (target == null) {
    if (isNullAware) {
      return null;
    }
    // C21 — Dart null-shorting: when an inner selector in this chain uses
    // `?.` (e.g. `a?.b.c.d` where `a == null`), every subsequent selector
    // up to the chain's termination point must also yield null instead of
    // throwing. The chain terminates at parentheses or non-selector
    // expressions; the helper walks the syntactic target chain and stops
    // there. Without this fix, `a?.b.c` throws "Cannot access property 'c'
    // on null" because the outer `.c` only sees `operator == '.'`.
    if (_chainHasNullAwareSelector(node.target)) {
      return null;
    }
    // G-DOV-10/11 FIX: Try extension lookup on nullable types before throwing
    final extensionMember = environment.findExtensionMember(
      target,
      propertyName,
      visitor: this,
    );
    if (extensionMember != null) {
      if (extensionMember is InterpretedFunction &&
          extensionMember.isGetter) {
        // Execute extension getter with 'this' bound to null
        final extensionEnv = Environment(enclosing: environment);
        extensionEnv.define('this', null);
        final prevEnv = environment;
        environment = extensionEnv;
        try {
          return extensionMember.call(this, [], {});
        } finally {
          environment = prevEnv;
        }
      }
      return extensionMember;
    }
    throw RuntimeD4rtException(
      "Cannot access property '$propertyName' on null. Use '?.' for null-aware access.",
    );
  }

  Logger.debug(
    "[SPropertyAccess: ${node.toString()}] Target type: ${target.runtimeType}, Target value: ${target.toString()}",
  );

  // GEN-100c: Handle prefixed import access (e.g., prefix.SomeClass.staticMember)
  // When the AST produces SPropertyAccess with a prefix target that resolved to an Environment,
  // delegate to that environment for member lookup.
  if (target is Environment) {
    Logger.debug(
      "[SPropertyAccess] Target resolved to Environment (prefixed import). Looking up '$propertyName'.",
    );
    try {
      final member = target.get(propertyName);
      if (member is InterpretedFunction && member.isGetter) {
        return member.call(this, [], {});
      }
      return member;
    } on RuntimeD4rtException catch (e) {
      throw RuntimeD4rtException(
        "Undefined member '$propertyName' in prefixed import: ${e.message}",
      );
    }
  }

  if (target is InterpretedInstance) {
    // Standard Instance Access: Try direct first, then extension
    try {
      final member = target.get(
        propertyName,
        visitor: this,
      ); // .get() handles inheritance
      if (member is InterpretedFunction && member.isGetter) {
        return member.call(
          this,
          [],
          {},
        ); // .get already returned bound getter
      } else {
        return member; // field value or bound method
      }
    } on RuntimeD4rtException catch (e) {
      // Try Extension Lookup Before Error
      if (e.message.contains("Undefined property '$propertyName'")) {
        Logger.debug(
          "[SPropertyAccess] Direct access failed for '$propertyName'. Trying extension lookup on ${target.runtimeType}.",
        );
        try {
          final extensionMember = environment.findExtensionMember(
            target,
            propertyName,
          );

          if (extensionMember is ExtensionMemberCallable) {
            if (extensionMember.isGetter) {
              Logger.debug(
                "[SPropertyAccess] Found extension getter '$propertyName'. Calling...",
              );
              // Getters are called with the instance as the first (and only) positional argument
              final extensionPositionalArgs = [target];
              return extensionMember.call(this, extensionPositionalArgs, {});
            } else if (!extensionMember.isOperator &&
                !extensionMember.isSetter) {
              // Return the extension method itself (it's not bound yet)
              Logger.debug(
                "[SPropertyAccess] Found extension method '$propertyName'. Returning callable.",
              );
              return extensionMember;
            }
          }
          // No suitable extension found, fall through to rethrow original error
          Logger.debug(
            "[SPropertyAccess] No suitable extension member found for '$propertyName'.",
          );
        } on RuntimeD4rtException catch (findError) {
          // Error during extension lookup itself
          Logger.debug(
            "[SPropertyAccess] Error during extension lookup for '$propertyName': ${findError.message}",
          );
          // Fall through to rethrow original error
        }
      }
      // Rethrow original error if it wasn't "Undefined property"or if extension lookup failed
      throw RuntimeD4rtException(
        "${e.message} (accessing property via SPropertyAccess '$propertyName')",
      );
    }
  } else if (target is InterpretedEnumValue) {
    try {
      // Get should execute the getter or return the field/bound method
      // Pass the visitor to potentially execute getters
      final member = target.get(propertyName, this);

      // Check if the result from get was already the final value (field or executed getter)
      // or if it's a bound method that shouldn't be called here.
      if (member is Callable) {
        // Property access should generally not return a raw callable method.
        // If get() returned a bound method, it means the propertyName matched a method name,
        // which is not what property access typically expects.
        // However, Dart allows accessing methods like properties to get a tear-off.
        // So, we return the bound method (Callable) here.
        Logger.debug(
          "[SPropertyAccess] Accessed enum method '$propertyName' on $target as tear-off. Returning bound method.",
        );
        return member;
      } else {
        // Must be a field value or the result of an executed getter.
        Logger.debug(
          "[SPropertyAccess] Accessed enum field/getter '$propertyName' on $target. Value: $member",
        );
        return member;
      }
    } on RuntimeD4rtException catch (e) {
      // Try Extension Getter if Direct Fails (similar to InterpretedInstance)
      if (e.message.contains("Undefined property '$propertyName'")) {
        Logger.debug(
          "[SPropertyAccess] Direct access failed for '$propertyName' on enum $target. Trying extension lookup...",
        );
        try {
          final extensionMember = environment.findExtensionMember(
            target,
            propertyName,
          );
          if (extensionMember is ExtensionMemberCallable) {
            if (extensionMember.isGetter) {
              Logger.debug(
                "[SPropertyAccess] Found extension getter '$propertyName' for enum. Calling...",
              );
              final extensionPositionalArgs = [target];
              return extensionMember.call(this, extensionPositionalArgs, {});
            } else if (!extensionMember.isOperator &&
                !extensionMember.isSetter) {
              Logger.debug(
                "[SPropertyAccess] Found extension method '$propertyName' for enum. Returning tear-off.",
              );
              return extensionMember;
            }
          }
          Logger.debug(
            "[SPropertyAccess] No suitable extension member found for '$propertyName' on enum.",
          );
        } on RuntimeD4rtException catch (findError) {
          Logger.debug(
            "[SPropertyAccess] Error during extension lookup for '$propertyName' on enum: ${findError.message}",
          );
        }
      }
      // Rethrow original error or error from extension lookup
      throw RuntimeD4rtException(
        "${e.message} (accessing property via SPropertyAccess '$propertyName' on enum value '$target')",
      );
    }
  } else if (target is InterpretedEnum) {
    // Accessing static member on the enum itself
    InterpretedFunction? staticGetter = target.staticGetters[propertyName];
    if (staticGetter != null) {
      // Call the static getter
      return staticGetter.call(this, [], {});
    }
    Object? staticField = target.staticFields[propertyName];
    if (target.staticFields.containsKey(propertyName)) {
      // Return static field value (could be null)
      return staticField;
    }
    InterpretedFunction? staticMethod = target.staticMethods[propertyName];
    if (staticMethod != null) {
      // Return the static method itself (tear-off)
      return staticMethod;
    }

    // Check mixins for static members (reverse order)
    for (final mixin in target.mixins.reversed) {
      final mixinStaticGetter = mixin.findStaticGetter(propertyName);
      if (mixinStaticGetter != null) {
        Logger.debug(
          "[SPropertyAccess] Found static getter '$propertyName' from mixin '${mixin.name}' for enum '${target.name}'",
        );
        return mixinStaticGetter.call(this, [], {});
      }

      final mixinStaticMethod = mixin.findStaticMethod(propertyName);
      if (mixinStaticMethod != null) {
        Logger.debug(
          "[SPropertyAccess] Found static method '$propertyName' from mixin '${mixin.name}' for enum '${target.name}'",
        );
        return mixinStaticMethod;
      }

      // Check static fields - use try/catch since getStaticField throws if not found
      try {
        final mixinStaticField = mixin.getStaticField(propertyName);
        Logger.debug(
          "[SPropertyAccess] Found static field '$propertyName' from mixin '${mixin.name}' for enum '${target.name}'",
        );
        return mixinStaticField;
      } on RuntimeD4rtException {
        // Continue to next mixin
      }
    }

    // Check for built-in 'values'
    if (propertyName == 'values') {
      return target.valuesList;
    }

    // Not found
    throw RuntimeD4rtException(
      "Undefined static property '$propertyName' on enum '${target.name}'.",
    );
  } else if (target is InterpretedClass) {
    // Static Access (no change)
    try {
      // Check static fields first (no inheritance for static fields in Dart)
      return target.getStaticField(propertyName);
    } on RuntimeD4rtException catch (_) {
      // If not a field, check static methods/getters
      InterpretedFunction? staticMember = target.findStaticGetter(
        propertyName,
      );
      staticMember ??= target.findStaticMethod(propertyName);

      if (staticMember != null) {
        if (staticMember.isGetter) {
          return staticMember.call(this, [], {}); // Call static getter
        } else {
          // Return static method function itself (not bound)
          return staticMember;
        }
      } else {
        // Cluster C32/C33: class-as-value (Type literal) semantics. When
        // a script binds a class identifier to a `Type` variable and
        // reads `Object` getters off it (`hashCode`, `runtimeType`), the
        // expected behaviour is `Object.hashCode` / `Object.runtimeType`
        // on the Type instance, not a static-member lookup on the class.
        // Fall back to the underlying Dart object's getters before
        // reporting an undefined-static error.
        if (propertyName == 'hashCode') {
          return target.hashCode;
        }
        if (propertyName == 'runtimeType') {
          return target.runtimeType;
        }
        throw RuntimeD4rtException(
          "Undefined static member '$propertyName' on class '${target.name}'.",
        );
      }
    }
  } else if (target is BoundSuper) {
    // Super Property Access
    final instance = target.instance;
    final startClass = target.startLookupClass;
    InterpretedClass? currentClass =
        startClass; // Start search from superclass

    while (currentClass != null) {
      // Check instance field in the bound instance's fields
      // Use the new public getter on the instance to access the field value
      try {
        final fieldValue = instance.getField(propertyName);
        // Field found on the instance, return its value regardless of where we are in the super hierarchy lookup
        return fieldValue;
      } on RuntimeD4rtException {
        // Field doesn't exist directly on the instance, continue searching for getters/methods
      }

      // Check instance getter in the current class in hierarchy
      final getter = currentClass.findInstanceGetter(propertyName);
      if (getter != null) {
        // Bind getter to the original instance and call
        return getter.bind(instance).call(this, [], {});
      }
      // Check instance method (less common for property access, but possible)
      final method = currentClass.findInstanceMethod(propertyName);
      if (method != null) {
        // Bind method to the original instance and return the bound method
        return method.bind(instance);
      }
      // Move up the hierarchy
      currentClass = currentClass.superclass;
    }
    // Not found in superclass hierarchy
    throw RuntimeD4rtException(
      "Undefined property '$propertyName' accessed via 'super' on instance of '${instance.klass.name}'.",
    );
  } else if (target is BridgedClass) {
    final bridgedClass = target;
    Logger.debug(
      "[SPropertyAccess] Static access on BridgedClass: ${bridgedClass.name}.$propertyName",
    );

    final staticGetter = bridgedClass.findStaticGetterAdapter(propertyName);
    if (staticGetter != null) {
      Logger.debug("[SPropertyAccess]   Found static getter adapter.");
      return wrapNativeReturnValue(
        staticGetter(this),
      ); // Call static getter adapter
    }

    final staticMethod = bridgedClass.findStaticMethodAdapter(propertyName);
    if (staticMethod != null) {
      Logger.debug("[SPropertyAccess]   Found static method adapter.");
      throw UnimplementedD4rtException(
        "Returning bridged static methods as values from SPropertyAccess is not yet supported.",
      );
      // return BridgedStaticMethodCallable(bridgedClass, staticMethod, propertyName);
    } else {
      // Cluster C32: class-as-value (Type literal) semantics. A script
      // that does `final Type t = SomeBridgedClass; t.hashCode;` expects
      // `Object.hashCode` on the Type instance, not a static-member
      // lookup on the bridged class. Fall back to the underlying Dart
      // object's getters before reporting an undefined-static error.
      if (propertyName == 'hashCode') {
        return bridgedClass.hashCode;
      }
      if (propertyName == 'runtimeType') {
        return bridgedClass.runtimeType;
      }
      throw RuntimeD4rtException(
        "Undefined static member '$propertyName' on bridged class '${bridgedClass.name}'.",
      );
    }
  } else if (toBridgedInstance(target).$2) {
    final bridgedInstance = toBridgedInstance(target).$1!;
    Logger.debug(
      "[SPropertyAccess] Access on BridgedInstance: ${bridgedInstance.bridgedClass.name}.$propertyName",
    );
    // GEN-075: Use nativeObject for Object properties
    switch (propertyName) {
      case 'runtimeType':
        return bridgedInstance.nativeObject.runtimeType;
      case 'hashCode':
        return bridgedInstance.nativeObject.hashCode;
      default:
    }
    final getterAdapter = bridgedInstance.bridgedClass
        .findInstanceGetterAdapter(propertyName);
    if (getterAdapter != null) {
      Logger.debug("[SPropertyAccess]   Found instance getter adapter.");
      return getterAdapter(
        this,
        bridgedInstance.nativeObject,
      ); // Call instance getter adapter
    }

    final methodAdapter = bridgedInstance.bridgedClass
        .findInstanceMethodAdapter(propertyName);
    if (methodAdapter != null) {
      Logger.debug(
        "[SPropertyAccess]   Found instance method adapter. Binding...",
      );
      // Return a callable bound to the instance
      return BridgedMethodCallable(
        bridgedInstance,
        methodAdapter,
        propertyName,
      );
    }

    // Cluster-12 (priority 3): Walk the registered supertype chain when
    // the leaf bridge has no matching getter/method. See
    // [InterpreterVisitorExtension.lookupOnBridgedSupertypes].
    final supertypeMatch =
        lookupOnBridgedSupertypes(bridgedInstance, propertyName);
    if (supertypeMatch.$2) {
      Logger.debug(
        "[SPropertyAccess]   Resolved '$propertyName' via supertype walk on '${bridgedInstance.bridgedClass.name}'.",
      );
      return supertypeMatch.$1;
    }

    // Try extension lookup before throwing error
    Logger.debug(
      "[SPropertyAccess] Direct access failed for '$propertyName' on BridgedInstance. Trying extension lookup...",
    );
    final extensionMember = environment.findExtensionMember(
      bridgedInstance,
      propertyName,
    );

    if (extensionMember is ExtensionMemberCallable) {
      if (extensionMember.isGetter) {
        Logger.debug(
          "[SPropertyAccess] Found extension getter '$propertyName' for BridgedInstance. Calling...",
        );
        // Getters are called with the native object as the first positional argument
        final extensionPositionalArgs = [bridgedInstance.nativeObject];
        return extensionMember.call(this, extensionPositionalArgs, {});
      } else if (!extensionMember.isOperator && !extensionMember.isSetter) {
        Logger.debug(
          "[SPropertyAccess] Found extension method '$propertyName' for BridgedInstance. Returning tear-off.",
        );
        return extensionMember;
      }
    }

    // Fix I: Check if the nativeObject is actually an Enum
    if (bridgedInstance.nativeObject is Enum) {
      final enumObj = bridgedInstance.nativeObject as Enum;
      // Fast path: built-in enum members. Keep this BEFORE the
      // BridgedEnumValue lookup — the lookup is O(N*M) over all registered
      // bridged enums and would dominate hot paths like `.name`/`.index`.
      switch (propertyName) {
        case 'name':
          return enumObj.name;
        case 'index':
          return enumObj.index;
        case 'hashCode':
          return enumObj.hashCode;
        case 'runtimeType':
          return enumObj.runtimeType;
        case 'toString':
          return NativeFunction(
            (visitor, args, namedArgs, typeArgs) => enumObj.toString(),
            arity: 0,
            name: 'toString',
          );
      }
      // Cluster-26 (Key.label dispatch): If the enum has a registered
      // BridgedEnumValue, dispatch through it so custom getters (e.g.
      // KeyEventType.label) registered on the BridgedEnumDefinition resolve.
      // The G-DCLI-05 prefix match in toBridgedClass can otherwise wrap a
      // native enum (KeyEventType) under an unrelated BridgedClass (Key).
      // Walks the current scope chain (which extends from the per-module
      // env where bridges register the BridgedEnum). Falls back to the
      // global env for runners that pre-populate enums there.
      final bridgedEnumValue =
          environment.getBridgedEnumValue(enumObj) ??
          globalEnvironment.getBridgedEnumValue(enumObj);
      if (bridgedEnumValue != null) {
        try {
          return bridgedEnumValue.get(propertyName);
        } on RuntimeD4rtException {
          // Fall through to "Undefined property" error below.
        }
      }
    }

    // D2: If the bridged instance wraps a D4InterpretedProxy (a native
    // proxy that holds a back-reference to the originating
    // [InterpretedInstance]), retry the property access on the wrapped
    // instance. Used for proxies like D4rtCustomPainter that route
    // bridged-callback dispatch to script-defined fields/getters
    // (e.g. `progress`) declared on the script's CustomPainter subclass.
    final native = bridgedInstance.nativeObject;
    if (native is D4InterpretedProxy) {
      final inner = native.d4rtInstance;
      if (inner is InterpretedInstance) {
        try {
          return inner.get(propertyName, visitor: this);
        } catch (_) {
          // Fall through to the "Undefined property" error below.
        }
      }
    }

    throw RuntimeD4rtException(
      "Undefined property or method '$propertyName' on bridged instance of '${bridgedInstance.bridgedClass.name}'.",
    );
  } else if (target is InterpretedRecord) {
    // Accessing field of a record
    final record = target;
    Logger.debug(
      "[SPropertyAccess] Access on InterpretedRecord: .$propertyName",
    );
    // Check if it's a positional field access (\$1, \$2, ...)
    if (propertyName.startsWith('\$') && propertyName.length > 1) {
      try {
        final index = int.parse(propertyName.substring(1)) - 1;
        if (index >= 0 && index < record.positionalFields.length) {
          return record.positionalFields[index];
        } else {
          throw RuntimeD4rtException(
            "Record positional field index \$$index out of bounds (0..${record.positionalFields.length - 1}).",
          );
        }
      } catch (e) {
        // Handle parse errors or other issues
        throw RuntimeD4rtException(
          "Invalid positional record field accessor '$propertyName'.",
        );
      }
    } else {
      // Check if it's a named field access
      if (record.namedFields.containsKey(propertyName)) {
        return record.namedFields[propertyName];
      } else {
        throw RuntimeD4rtException(
          "Record has no field named '$propertyName'. Available fields: ${record.namedFields.keys.join(', ')}",
        );
      }
    }
  } else if (target is Record) {
    // E6: native Dart Record (e.g., produced by `Iterable.indexed`).
    // Route positional access via dynamic dispatch — see
    // _accessNativeRecordField for the limitations.
    return _accessNativeRecordField(target, propertyName);
  } else if (target is BridgedEnumValue) {
    return target.get(propertyName);
  } else if (target is BridgedEnum) {
    Logger.debug(
      "[SPropertyAccess] Accessing value on BridgedEnum: ${target.name}.$propertyName",
    );
    final enumValue = target.getValue(propertyName);
    if (enumValue != null) {
      return enumValue; // Return the BridgedEnumValue
    } else {
      throw RuntimeD4rtException(
        "Undefined enum value '$propertyName' on bridged enum '${target.name}'.",
      );
    }
  } else if (target is BoundBridgedSuper) {
    final instance = target.instance; // The interpreted 'this' instance
    final bridgedSuper = target.startLookupClass;
    // RC-6: Use nativeProxy as fallback when bridgedSuperObject is null.
    // This supports abstract class adapters (like _InterpretedState for State).
    final nativeSuperObject =
        instance.bridgedSuperObject ?? instance.nativeProxy;

    if (nativeSuperObject == null) {
      throw RuntimeD4rtException(
        "Internal error: Cannot access super property '$propertyName' on bridged superclass '${bridgedSuper.name}' because the native super object is missing.",
      );
    }

    // Try the bridged getter
    final getterAdapter = bridgedSuper.findInstanceGetterAdapter(
      propertyName,
    );
    if (getterAdapter != null) {
      try {
        return getterAdapter(this, nativeSuperObject);
      } catch (e, s) {
        Logger.error(
          "Native exception during super access to bridged getter '${bridgedSuper.name}.$propertyName': $e\n$s",
        );
        throw RuntimeD4rtException(
          "Native error during super access to bridged getter '$propertyName': $e",
          originalException: e,
        );
      }
    }

    // Try the bridged method (for tear-off)
    final methodAdapter = bridgedSuper.findInstanceMethodAdapter(
      propertyName,
    );
    if (methodAdapter != null) {
      // Return a callable bound to the native object
      return BridgedSuperMethodCallable(
        nativeSuperObject,
        methodAdapter,
        propertyName,
        bridgedSuper.name,
      );
    }

    // Not found
    throw RuntimeD4rtException(
      "Undefined property or method '$propertyName' accessed via 'super' on bridged superclass '${bridgedSuper.name}'.",
    );
  } else if (target is Callable) {
    // ENG-006: Handle property access on function objects (closures, NativeFunctions)
    Logger.debug(
      "[SPropertyAccess] Access on Callable: ${target.runtimeType}.$propertyName",
    );
    switch (propertyName) {
      case 'runtimeType':
        return target.runtimeType;
      case 'hashCode':
        return target.hashCode;
      case 'toString':
        return NativeFunction(
          (visitor, args, namedArgs, typeArgs) => target.toString(),
          arity: 0,
          name: 'toString',
        );
      case 'call':
        return target; // Tear-off
      default:
        throw RuntimeD4rtException(
          "Undefined property '$propertyName' on function object (${target.runtimeType}).",
        );
    }
  } else {
    // Check if target is a native enum that has been bridged
    final bridgedEnumValue = environment.getBridgedEnumValue(target);
    if (bridgedEnumValue != null) {
      Logger.debug(
        "[SPropertyAccess] Found bridged enum value for native enum ${target.runtimeType}",
      );
      try {
        return bridgedEnumValue.get(propertyName);
      } catch (e) {
        throw RuntimeD4rtException(
          "Undefined property '$propertyName' on bridged enum value '${bridgedEnumValue.name}'.",
        );
      }
    }

    // Fix I: Generic Enum property access for raw Enum targets
    if (target is Enum) {
      switch (propertyName) {
        case 'name':
          return (target).name;
        case 'index':
          return (target).index;
        case 'hashCode':
          return target.hashCode;
        case 'runtimeType':
          return target.runtimeType;
        case 'toString':
          return NativeFunction(
            (visitor, args, namedArgs, typeArgs) => target.toString(),
            arity: 0,
            name: 'toString',
          );
      }
    }

    Logger.debug(
      "[SPropertyAccess] Looking for extension getter '$propertyName' for target type ${target.runtimeType}.",
    );
    final extensionCallable = environment.findExtensionMember(
      target,
      propertyName,
    );

    if (extensionCallable is ExtensionMemberCallable &&
        extensionCallable.isGetter) {
      Logger.debug(
        "[SPropertyAccess] Found extension getter '$propertyName'. Calling...",
      );
      // Prepend the target instance to the positional arguments for the extension call
      final extensionPositionalArgs = [
        target,
      ]; // Getters take no explicit args
      try {
        // Call the extension getter
        return extensionCallable.call(this, extensionPositionalArgs, {});
      } on ReturnException catch (e) {
        return e.value;
      } catch (e) {
        throw RuntimeD4rtException(
          "Error executing extension getter '$propertyName': $e",
        );
      }
    } else {
      // GEN-C3c: Universal Object-member fallback for arbitrary native
      // targets. Every Dart Object has `toString`, `hashCode`, and
      // `runtimeType`; these must always resolve regardless of whether the
      // runtime type is bridged or not. Mirrors the BridgedInstance branch
      // (GEN-075) above and the Callable branch at ENG-006. `target` is
      // non-null here — the early-return at the top of visitPropertyAccess
      // handles the null-receiver case.
      switch (propertyName) {
        case 'hashCode':
          return target.hashCode;
        case 'runtimeType':
          return target.runtimeType;
        case 'toString':
          return NativeFunction(
            (visitor, args, namedArgs, typeArgs) => target.toString(),
            arity: 0,
            name: 'toString',
          );
      }

      // No extension getter found either, rethrow the original stdlib error
      Logger.debug(
        "[SPropertyAccess] Extension getter '$propertyName' not found. Rethrowing original error.",
      );
      throw RuntimeD4rtException(
        "Undefined property or method '$propertyName' on ${target.runtimeType}.",
      );
    }
  }
}