defineGlobal method

void defineGlobal(
  1. String name,
  2. dynamic value
)

Defines or updates a global variable in the root environment.

This method provides precise control for global variable assignments by always operating on the root (global) environment, completely ignoring any local variables with the same name in the current scope chain.

Behavior:

  • Always operates on the root environment (root)
  • Updates existing global variable if it exists
  • Creates new global variable if it doesn't exist
  • Completely ignores local variables with same name
  • Respects const variable restrictions for globals
  • Handles to-be-closed variable tracking in root environment

Usage: When we specifically want to create or update a global variable, regardless of whether local variables with the same name exist.

Why needed: The original define() method would find local variables first and update them instead of creating/updating globals. This method ensures we can always target the global environment specifically.

Example scenario:

local x = 1      -- Local variable exists
_G.x = 2         -- defineGlobal() should update global, not local

Note: This is used when assignment logic determines that a global assignment is intended (e.g., no local variable exists to update).

Implementation

void defineGlobal(String name, dynamic value) {
  Logger.debugLazy(
    () => "Defining global variable '$name' = $value",
    category: 'Env',
  );

  final rootEnv = root;

  // Check if global variable already exists
  if (rootEnv.values.containsKey(name)) {
    final box = rootEnv.values[name]!;
    if (box.preventsAssignment) {
      Logger.debugLazy(
        () => "Attempt to modify const global variable '$name'",
        category: 'Env',
      );
      throw LuaError("attempt to assign to const variable '$name'");
    }
    if (!_tryFastReplaceBoxValue(box, value)) {
      box.value = value;
    }
    Logger.debugLazy(
      () =>
          "Updated global variable '$name' to $value in root env "
          '(${rootEnv.hashCode})',
      category: 'Env',
    );
  } else {
    // Create new global variable
    Logger.debugLazy(
      () =>
          "Creating new global variable '$name' with value type "
          '${value.runtimeType} (is GCObject: ${value is GCObject})',
      category: 'Env',
    );
    final box = Box(value, interpreter: rootEnv.interpreter);
    rootEnv.values[name] = box;
    rootEnv._noteChildReference(box);
    Logger.debugLazy(
      () =>
          "Created new global variable '$name' = $value in root env "
          '(${rootEnv.hashCode})',
      category: 'Env',
    );
  }

  rootEnv._updateCredits();

  // Keep the underlying _G table in sync so reads via _G[k] see updates
  _syncGlobalTableEntry(name, rootEnv.values[name]!.value);

  // Track to-be-closed variables in root environment
  if (rootEnv.values[name]!.isToBeClose) {
    rootEnv.toBeClosedVars.add(name);
    Logger.debugLazy(
      () => "Added '$name' to to-be-closed variables list in root env",
      category: 'Env',
    );
  }
}