function pushdown_computables (instance, scope_map) {
  var valid_scopes;

  if (instance instanceof ScopeInstance) {
    valid_scopes = [instance.get_scope_definition()];
  } else {
    valid_scopes = instance.get_referenced_scopes().filter(is_valid_scope);
  }

  var pushed = 0;
  var pushdown_data = [];
  var immovables = find_immovable_inputs(instance, valid_scopes);

  valid_scopes.forEach(function (scope) {
    var scope_id = scope.get_identity();
    var inputs = get_instance_inputs_by_scope(instance, scope);
    var pushdowns = find_pushdowns(instance, inputs, immovables);

    var new_computables = pushdowns.computables;
    if (!new_computables.length) return;

    var new_inputs = pushdowns.inputs;
    var new_scope = create_pushdown_scope(scope, new_computables, new_inputs, inputs);
    scope_map[scope_id] = new_scope;

    pushed += new_computables.length;
    scope.flush();

    pushdown_data.push({
      id: scope_id,
      new_scope: new_scope,
      computables: new_computables,
      inputs: new_inputs,
    });
  });

  // If no pushdown optimization was possible, abort.
  if (!pushdown_data.length) return pushed;

  var new_instance = create_pushdown_instance(instance, pushdown_data);

  // Reset all dependees to new_instance.
  while (instance.get_dependee_count()) {
    var dependee = instance.get_dependee(0);
    var input_indices = dependee.get_input_indices(instance);
    for (var i = 0; i < input_indices.length; ++i) {
      var index = input_indices[i];
      dependee.set_input(index, new_instance);
    }
  }

  instance.destroy();

  // Destroy computables that were pushed down.
  pushdown_data.forEach(function (data) {
    data.computables.forEach(function (computable) {
      computable.destroy();
    });
  });

  return pushed;
}
/**
 * @param {ScopeInstance|PolymorphicScopeInstance} instance
 * @param {Object} scope_map Hash for tracking replaced scopes
 * @returns {number} How many computables were pushed
 */
@param {ScopeInstance|PolymorphicScopeInstance} instance
@param {Object} scope_map Hash for tracking replaced scopes
@returns {number} How many computables were pushed
@param { ScopeInstance|PolymorphicScopeInstance } instance @param { Object } scope_map Hash for tracking replaced scopes @returns { number } How many computables were pushed
function pushdown_computables (instance, scope_map) {
  var valid_scopes;
  if (instance instanceof ScopeInstance) {
    valid_scopes = [instance.get_scope_definition()];
  } else {
    valid_scopes = instance.get_referenced_scopes().filter(is_valid_scope);
  }
  var pushed = 0;
  var pushdown_data = [];
  var immovables = find_immovable_inputs(instance, valid_scopes);
  valid_scopes.forEach(function (scope) {
    var scope_id = scope.get_identity();
    var inputs = get_instance_inputs_by_scope(instance, scope);
    var pushdowns = find_pushdowns(instance, inputs, immovables);
    var new_computables = pushdowns.computables;
    if (!new_computables.length) return;
    var new_inputs = pushdowns.inputs;
    var new_scope = create_pushdown_scope(scope, new_computables, new_inputs, inputs);
    scope_map[scope_id] = new_scope;
    pushed += new_computables.length;
    scope.flush();
    pushdown_data.push({
      id: scope_id,
      new_scope: new_scope,
      computables: new_computables,
      inputs: new_inputs,
    });
  });
  if (!pushdown_data.length) return pushed;
  var new_instance = create_pushdown_instance(instance, pushdown_data);
  while (instance.get_dependee_count()) {
    var dependee = instance.get_dependee(0);
    var input_indices = dependee.get_input_indices(instance);
    for (var i = 0; i < input_indices.length; ++i) {
      var index = input_indices[i];
      dependee.set_input(index, new_instance);
    }
  }
  instance.destroy();
  pushdown_data.forEach(function (data) {
    data.computables.forEach(function (computable) {
      computable.destroy();
    });
  });
  return pushed;
}
function pushdown_computables ( instance , scope_map ) { var valid_scopes ; if ( instance instanceof ScopeInstance ) { valid_scopes = [ instance . get_scope_definition ( ) ] ; } else { valid_scopes = instance . get_referenced_scopes ( ) . filter ( is_valid_scope ) ; } var pushed = 0 ; var pushdown_data = [ ] ; var immovables = find_immovable_inputs ( instance , valid_scopes ) ; valid_scopes . forEach ( function ( scope ) { var scope_id = scope . get_identity ( ) ; var inputs = get_instance_inputs_by_scope ( instance , scope ) ; var pushdowns = find_pushdowns ( instance , inputs , immovables ) ; var new_computables = pushdowns . computables ; if ( ! new_computables . length ) return ; var new_inputs = pushdowns . inputs ; var new_scope = create_pushdown_scope ( scope , new_computables , new_inputs , inputs ) ; scope_map [ scope_id ] = new_scope ; pushed += new_computables . length ; scope . flush ( ) ; pushdown_data . push ( { id : scope_id , new_scope : new_scope , computables : new_computables , inputs : new_inputs , } ) ; } ) ; if ( ! pushdown_data . length ) return pushed ; var new_instance = create_pushdown_instance ( instance , pushdown_data ) ; while ( instance . get_dependee_count ( ) ) { var dependee = instance . get_dependee ( 0 ) ; var input_indices = dependee . get_input_indices ( instance ) ; for ( var i = 0 ; i < input_indices . length ; ++ i ) { var index = input_indices [ i ] ; dependee . set_input ( index , new_instance ) ; } } instance . destroy ( ) ; pushdown_data . forEach ( function ( data ) { data . computables . forEach ( function ( computable ) { computable . destroy ( ) ; } ) ; } ) ; return pushed ; }
@param {ScopeInstance|PolymorphicScopeInstance} instance
 @param {Object} scope_map Hash for tracking replaced scopes
 @returns {number} How many computables were pushed
@param { ScopeInstance|PolymorphicScopeInstance } instance @param { Object } scope_map Hash for tracking replaced scopes @returns { number } How many computables were pushed
// If no pushdown optimization was possible, abort. // Reset all dependees to new_instance. // Destroy computables that were pushed down.