NamespaceResolver::Ptr GenericNamespaceResolver::defaultXSLTBindings()
{
    Bindings list;

    list.insert(StandardPrefixes::xml,    StandardNamespaces::xml);
    list.insert(StandardPrefixes::empty,  StandardNamespaces::empty);

    return NamespaceResolver::Ptr(new GenericNamespaceResolver(list));
}
Пример #2
0
std::vector<std::string> SubscriberManager::subscriptions_to_remove(const Bindings& orig_bindings,
                                                                    const Subscriptions& orig_subscriptions,
                                                                    const Bindings& bindings_to_update,
                                                                    const std::vector<std::string> binding_ids_to_remove)
{
  std::vector<std::string> subscription_ids_to_remove;
  std::set<std::string> missing_uris;

  // Store off the contact URIs of bindings to be removed. Any subscriptions
  // sharing any of these contact URIs will be removed.
  for (std::string binding_id : binding_ids_to_remove)
  {
    Bindings::const_iterator b = orig_bindings.find(binding_id);
    if (b != orig_bindings.end())
    {
      missing_uris.insert(b->second->_uri);
    }
  }

  // Store off the original contact URI of bindings where the contact is about
  // to be changed. Any subscriptions that share any of the original contact
  // URIs will be removed.
  for (BindingPair bp : bindings_to_update)
  {
    Bindings::const_iterator b = orig_bindings.find(bp.first);
    if ((b != orig_bindings.end()) &&
        (b->second->_uri != bp.second->_uri))
    {
      missing_uris.insert(b->second->_uri);
    }
  }

  // Loop over the subscriptions. If any have the same contact as one of the
  // missing URIs, the subscription should be removed.
  for (SubscriptionPair sp : orig_subscriptions)
  {
    if (missing_uris.find(sp.second->_req_uri) != missing_uris.end())
    {
      TRC_DEBUG("Subscription %s is being removed because the binding that shares"
                " its contact URI %s is being removed or changing contact URI",
                sp.first.c_str(),
                sp.second->_req_uri.c_str());
      subscription_ids_to_remove.push_back(sp.first);
    }
  }

  return subscription_ids_to_remove;
}
	void Statement::BindParams( const Bindings& bindings )
	{
		if (m_kQueryParts.size())
		{
			FV_ASSERT( bindings.size() == m_kQueryParts.size()-1 );
			m_kParams = bindings;
		}
	}
Пример #4
0
void Bindings::construct(const Args& args)
{
    Isolate* isolate(args.GetIsolate());
    HandleScope scope(isolate);

    if (args.IsConstructCall())
    {
        // Invoked as constructor with 'new'.
        Bindings* obj = new Bindings();
        obj->Wrap(args.Holder());
        args.GetReturnValue().Set(args.Holder());
    }
    else
    {
        // Invoked as a function, turn into construct call.
        Local<Function> ctor(Local<Function>::New(isolate, constructor));
        args.GetReturnValue().Set(ctor->NewInstance());
    }
}
Пример #5
0
bool RegDB::getUnexpiredContactsUserContaining(const string& matchIdentity, int timeNow, Bindings& bindings) const
{
	mongo::BSONObj query = BSON("expirationTime" << BSON_GREATER_THAN(timeNow));

	mongo::ScopedDbConnection conn(_info.getConnectionString());
	auto_ptr<mongo::DBClientCursor> pCursor = conn->query(_info.getNS(), query);
	if (pCursor.get() && pCursor->more())
	{
		while (pCursor->more())
		{
			RegBinding binding(pCursor->next());
			if (binding.getContact().find(matchIdentity) != string::npos)
				bindings.push_back(binding);
		}
		conn.done();
		return bindings.size() > 0;
	}

	conn.done();
	return false;
}
NamespaceResolver::Ptr GenericNamespaceResolver::defaultXQueryBindings()
{
    Bindings list;

    list.insert(StandardPrefixes::xml,    StandardNamespaces::xml);
    list.insert(StandardPrefixes::xs,     StandardNamespaces::xs);
    list.insert(StandardPrefixes::xsi,    StandardNamespaces::xsi);
    list.insert(StandardPrefixes::fn,     StandardNamespaces::fn);
    list.insert(StandardPrefixes::local,  StandardNamespaces::local);
    list.insert(StandardPrefixes::empty,  StandardNamespaces::empty);

    return NamespaceResolver::Ptr(new GenericNamespaceResolver(list));
}
Пример #7
0
// TODO : Unclear how big this dataset would be, decide if this should be removed
bool RegDB::getUnexpiredContactsInstrument(const string& instrument, int timeNow, Bindings& bindings) const
{
	mongo::BSONObj query = BSON(
			"instrument" << instrument <<
			"expirationTime" << BSON_GREATER_THAN(timeNow));

	mongo::ScopedDbConnection conn(_info.getConnectionString());
	auto_ptr<mongo::DBClientCursor> pCursor = conn->query(_info.getNS(), query);
	if (pCursor.get() && pCursor->more())
	{
		while (pCursor->more()) {
			bindings.push_back(RegBinding(pCursor->next()));
		}
		conn.done();
		return true;
	}

	conn.done();
	return false;
}
Пример #8
0
bool RegDB::getUnexpiredContactsUser(const string& identity, int timeNow, Bindings& bindings) const
{
	static string gruuPrefix = GRUU_PREFIX;

	bool isGruu = identity.substr(0, gruuPrefix.size()) == gruuPrefix;
	mongo::BSONObj query;

	if (isGruu)
	{
		string searchString(identity);
		searchString += ";";
		searchString += SIP_GRUU_URI_PARAM;
		query = BSON(
				"gruu" << searchString <<
				"expirationTime" << BSON_GREATER_THAN(timeNow));
	}
	else
	{
		query = BSON(
				"identity" << identity <<
				"expirationTime" << BSON_GREATER_THAN(timeNow));
	}

	mongo::ScopedDbConnection conn(_info.getConnectionString());
	auto_ptr<mongo::DBClientCursor> pCursor = conn->query(_info.getNS(), query);
	if (pCursor.get() && pCursor->more())
	{
		while (pCursor->more())
		{
			bindings.push_back(RegBinding(pCursor->next()));
		}
		conn.done();
		return true;
	}

	conn.done();
	return false;
}
Пример #9
0
HTTPCode SubscriberManager::register_subscriber_internal(const std::string& aor_id,
                                                         const std::string& server_name,
                                                         const AssociatedURIs& associated_uris,
                                                         const Bindings& add_bindings,
                                                         Bindings& all_bindings,
                                                         HSSConnection::irs_info& irs_info,
                                                         bool retry,
                                                         SAS::TrailId trail)
{
  TRC_DEBUG("Registering AoR %s for the first time", aor_id.c_str());

  int now = time(NULL);

  // We are registering a subscriber for the first time, so there is no stored
  // AoR. PUT the new bindings to S4.
  AoR* orig_aor = NULL;
  AoR* updated_aor = NULL;

  // We may have been called with no bindings to update. This is the case when
  // we receive a fetch bindings register from a subscriber that is unregistered.
  // In that situation, we do not have any data to PUT to S4, but we should
  // deregister the subscriber with the HSS.
  if (!add_bindings.empty())
  {
    PatchObject patch_object;
    build_patch(patch_object,
                add_bindings,
                associated_uris);

    updated_aor = new AoR(aor_id);
    updated_aor->patch_aor(patch_object);
    updated_aor->_scscf_uri = server_name;

    // PUT a new AoR.
    HTTPCode rc = _s4->handle_put(aor_id,
                                  *updated_aor,
                                  trail);

    // If the PUT resulted in precondition failed (which happens if there is
    // already an AoR in the store), we retry with a reregister.
    if ((rc == HTTP_PRECONDITION_FAILED) && (retry))
    {
      TRC_DEBUG("Registering AoR %s failed with 412 PRECONDITION FAILED - retry with reregister",
                aor_id.c_str());
      delete updated_aor; updated_aor = NULL;
      return reregister_subscriber_internal(aor_id,
                                            server_name,
                                            associated_uris,
                                            add_bindings,
                                            {},
                                            all_bindings,
                                            irs_info,
                                            false,
                                            trail);
    }
    else if (rc != HTTP_OK)
    {
      TRC_DEBUG("Registering AoR %s failed with return code %d",
                aor_id.c_str(),
                rc);

      delete updated_aor; updated_aor = NULL;
      return rc;
    }

    log_updated_bindings(*updated_aor, add_bindings, now);

    // Get all bindings to return to the caller
    all_bindings = SubscriberDataUtils::copy_active_bindings(updated_aor->bindings(),
                                                             now,
                                                             trail);
  }
  else
  {
    // The was nothing to store for this subscriber so we should deregister the
    // subscriber with the HSS since they will have previously been registered
    // incorrectly when the registrar was figuring out the default public ID.
    if (all_bindings.empty())
    {
      HTTPCode rc = deregister_with_hss(aor_id,
                                        HSSConnection::DEREG_USER,
                                        server_name,
                                        irs_info,
                                        trail);
      if (rc != HTTP_OK)
      {
        TRC_DEBUG("Failed to deregister subscriber %s with HSS",
                  aor_id.c_str());
        delete orig_aor; orig_aor = NULL;
        delete updated_aor; updated_aor = NULL;
        return rc;
      }
    }
  }

  delete orig_aor; orig_aor = NULL;
  delete updated_aor; updated_aor = NULL;

  return HTTP_OK;
}
Пример #10
0
HTTPCode SubscriberManager::remove_bindings(const std::string& public_id,
                                            const std::vector<std::string>& binding_ids,
                                            const SubscriberDataUtils::EventTrigger& event_trigger,
                                            Bindings& bindings,
                                            SAS::TrailId trail)
{
  TRC_DEBUG("Removing bindings from IMPU %s", public_id.c_str());

  int now = time(NULL);

  // Get cached subscriber information from the HSS.
  std::string aor_id;
  HSSConnection::irs_info irs_info;
  HTTPCode rc = get_cached_default_id(public_id,
                                      aor_id,
                                      irs_info,
                                      trail);
  if (rc != HTTP_OK)
  {
    return rc;
  }

  // Get the original AoR from S4.
  AoR* orig_aor = NULL;
  uint64_t unused_version;
  rc = _s4->handle_get(aor_id,
                       &orig_aor,
                       unused_version,
                       trail);

  if (rc != HTTP_OK)
  {
    delete orig_aor; orig_aor = NULL;
    if (rc == HTTP_NOT_FOUND)
    {
      // If there is no AoR, we still count that as a success.
      TRC_DEBUG("Removing bindings for AoR %s succeeded because no bindings are present in the store",
                aor_id.c_str());
      return HTTP_OK;
    }

    TRC_DEBUG("Removing bindings for AoR %s failed during GET with return code %d",
              aor_id.c_str(),
              rc);
    return rc;
  }

  // We log removed bindings before writing to the store so that in the case
  // that the write fails, our estimate of how many active bindings we have will
  // be an underestimate, not an overestimate.
  log_removed_bindings(*orig_aor,
                       binding_ids);

  // Check if there are any subscriptions that share the same contact as
  // the removed bindings, and delete them too.
  std::vector<std::string> subscription_ids_to_remove =
                             subscriptions_to_remove(orig_aor->bindings(),
                                                     orig_aor->subscriptions(),
                                                     Bindings(),
                                                     binding_ids);

  PatchObject patch_object;
  build_patch(patch_object,
              binding_ids,
              subscription_ids_to_remove,
              irs_info._associated_uris);

  // PATCH the existing AoR.
  AoR* updated_aor = NULL;
  rc = _s4->handle_patch(aor_id,
                         patch_object,
                         &updated_aor,
                         trail);

  if (rc != HTTP_OK)
  {
    TRC_DEBUG("Removing bindings for AoR %s failed during PATCH with return code %d",
              aor_id.c_str(),
              rc);
    delete orig_aor; orig_aor = NULL;
    delete updated_aor; updated_aor = NULL;
    return rc;
  }

  log_subscriptions(aor_id,
                    *orig_aor,
                    *updated_aor,
                    subscription_ids_to_remove,
                    now);

  // Get all bindings to return to the caller
  bindings = SubscriberDataUtils::copy_active_bindings(updated_aor->bindings(),
                                                       now,
                                                       trail);

  send_notifys(aor_id,
               orig_aor,
               updated_aor,
               event_trigger,
               now,
               trail);

  // Update HSS if all bindings expired.
  if (bindings.empty())
  {
    // If this action was not triggered by the HSS e.g. because of an RTR, we
    // should dergister with the HSS.
    if (event_trigger != SubscriberDataUtils::EventTrigger::HSS)
    {
      std::string dereg_reason = (event_trigger == SubscriberDataUtils::EventTrigger::USER) ?
                                   HSSConnection::DEREG_USER : HSSConnection::DEREG_ADMIN;
      rc = deregister_with_hss(aor_id,
                               dereg_reason,
                               updated_aor->_scscf_uri,
                               irs_info,
                               trail);
      if (rc != HTTP_OK)
      {
        TRC_DEBUG("Failed to deregister subscriber %s with HSS",
                  aor_id.c_str());
        delete orig_aor; orig_aor = NULL;
        delete updated_aor; updated_aor = NULL;
        return rc;
      }
    }

    // Send 3rd party deREGISTERs.
    _registration_sender->deregister_with_application_servers(public_id,
                                                              irs_info._service_profiles[public_id],
                                                              trail);
  }

  delete orig_aor; orig_aor = NULL;
  delete updated_aor; updated_aor = NULL;

  return HTTP_OK;
}
Пример #11
0
HTTPCode SubscriberManager::reregister_subscriber_internal(const std::string& aor_id,
                                                           const std::string& server_name,
                                                           const AssociatedURIs& associated_uris,
                                                           const Bindings& updated_bindings,
                                                           const std::vector<std::string>& binding_ids_to_remove,
                                                           Bindings& all_bindings,
                                                           HSSConnection::irs_info& irs_info,
                                                           bool retry,
                                                           SAS::TrailId trail)
{
  TRC_DEBUG("Reregistering AoR %s", aor_id.c_str());

  int now = time(NULL);

  AoR* orig_aor = NULL;
  uint64_t unused_version;
  HTTPCode rc = _s4->handle_get(aor_id,
                                &orig_aor,
                                unused_version,
                                trail);

  // We are reregistering a subscriber, so there must be an existing AoR in the
  // store.
  AoR* updated_aor = NULL;
  if ((rc == HTTP_NOT_FOUND) && (retry))
  {
    TRC_DEBUG("Reregistering AoR %s failed with 404 NOT FOUND - retry with register",
              aor_id.c_str());
    return register_subscriber_internal(aor_id,
                                        server_name,
                                        associated_uris,
                                        updated_bindings,
                                        all_bindings,
                                        irs_info,
                                        false,
                                        trail);
  }
  else if (rc != HTTP_OK)
  {
    TRC_DEBUG("Reregistering AoR %s failed during GET with return code %d",
              aor_id.c_str(),
              rc);
    delete orig_aor; orig_aor = NULL;
    return rc;
  }

  // Check if there are any subscriptions that share the same contact as
  // the removed bindings, and delete them too.
  std::vector<std::string> subscription_ids_to_remove =
                             subscriptions_to_remove(orig_aor->bindings(),
                                                     orig_aor->subscriptions(),
                                                     updated_bindings,
                                                     binding_ids_to_remove);

  // We log removed bindings before writing to the store so that in the case
  // that the write fails, our estimate of how many active bindings we have will
  // be an underestimate, not an overestimate.
  log_removed_bindings(*orig_aor,
                       binding_ids_to_remove);

  PatchObject patch_object;
  build_patch(patch_object,
              updated_bindings,
              binding_ids_to_remove,
              subscription_ids_to_remove,
              associated_uris);

  // PATCH the existing AoR.
  rc = _s4->handle_patch(aor_id,
                         patch_object,
                         &updated_aor,
                         trail);

  // If we didn't find an existing AoR, that means the subscriber does not
  // currently exist. We might want to retry by registering the subscriber
  // afresh.
  if ((rc == HTTP_NOT_FOUND) && (retry))
  {
    TRC_DEBUG("Reregistering AoR %s failed with 404 NOT FOUND - retry with register",
              aor_id.c_str());
    delete orig_aor; orig_aor = NULL;
    return register_subscriber_internal(aor_id,
                                        server_name,
                                        associated_uris,
                                        updated_bindings,
                                        all_bindings,
                                        irs_info,
                                        false,
                                        trail);
  }
  else if (rc != HTTP_OK)
  {
    TRC_DEBUG("Reregistering AoR %s failed during PATCH with return code %d",
              aor_id.c_str(),
              rc);
    delete orig_aor; orig_aor = NULL;
    delete updated_aor; updated_aor = NULL;
    return rc;
  }

  log_updated_bindings(*updated_aor, updated_bindings, now);

  log_subscriptions(aor_id,
                    *orig_aor,
                    *updated_aor,
                    subscription_ids_to_remove,
                    now);

  // Get all bindings to return to the caller
  all_bindings = SubscriberDataUtils::copy_active_bindings(updated_aor->bindings(),
                                                           now,
                                                           trail);

  send_notifys(aor_id,
               orig_aor,
               updated_aor,
               SubscriberDataUtils::EventTrigger::USER,
               now,
               trail);

  // Update HSS if all bindings expired.
  if (all_bindings.empty())
  {
    rc = deregister_with_hss(aor_id,
                             HSSConnection::DEREG_USER,
                             updated_aor->_scscf_uri,
                             irs_info,
                             trail);
    if (rc != HTTP_OK)
    {
      TRC_DEBUG("Failed to deregister subscriber %s with HSS",
                aor_id.c_str());
      delete orig_aor; orig_aor = NULL;
      delete updated_aor; updated_aor = NULL;
      return rc;
    }
  }

  delete orig_aor; orig_aor = NULL;
  delete updated_aor; updated_aor = NULL;

  return HTTP_OK;
}
Пример #12
0
Value Interpreter::exec(const InstructionList &instructions, Bindings &bindings)
{
	Stack stack;
	ClosureValues closureValues;

	for(InstructionList::const_iterator it = instructions.begin() ; it != instructions.end() ; ++it)
	{
		Instruction::Type type = it->type();
		const Value &value = it->value();
		switch(type)
		{
		case Instruction::PUSH:
			if(settings_.trace)
			{				
				std::cout << "DEBUG: " << it->sourceLocation() << " push " << value << '\n';
			}
			stack.push_back(value);
			break;
		case Instruction::CALL:
			{
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " call " << value << '\n';
				}
				Value result = handleFunction(it->sourceLocation(), value, stack, bindings);
				stack.push_back(result);
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " return value " << result << '\n';
				}
			}
			break;
		case Instruction::JUMP:
			{
				int instructionsToSkip = getInstructionsToSkip(type, value);
				int remaining = instructions.end() - it;
				if(remaining < instructionsToSkip)
				{
					throw CompilerBug("insufficient instructions available to skip! (remaining: " + str(remaining) + " < instructionsToSkip: " + str(instructionsToSkip) + ")");
				}

				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " jumping back " << instructionsToSkip << '\n';
				}
				it += instructionsToSkip;
			}
			break;
		case Instruction::LOOP:
			{
				int instructionsToSkip = getInstructionsToSkip(type, value);
				int instructionsAvailable = instructions.size(); // Note: signed type is important!
				if(instructionsAvailable < instructionsToSkip)
				{
					throw CompilerBug("insufficient instructions available to loop! (instructionsToSkip: " + str(instructionsToSkip) + " > instructions.size(): " + str(instructions.size()) + ")");
				}

				if(settings_.trace)
				{				
					std::cout << "DEBUG: " << it->sourceLocation() << " looping back " << instructionsToSkip << " instructions\n";
				}
				it -= instructionsToSkip;
			}
			break;
		case Instruction::CLOSE:
			{
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " close " << value << '\n';
				}
				Value result = handleClose(value, stack, closureValues, bindings);
				stack.push_back(result);
			}
			break;
		case Instruction::COND_JUMP:
			{
				if(stack.empty())
				{
					throw CompilerBug("empty stack when testing conditional jump");
				}
				int instructionsToSkip = getInstructionsToSkip(type, value);
				int remaining = instructions.end() - it;
				if(remaining < instructionsToSkip)
				{
					throw CompilerBug("insufficient instructions available to skip! (remaining: " + str(remaining) + " < instructionsToSkip: " + str(instructionsToSkip) + ")");
				}

				Value top = pop(stack);
				if(settings_.trace)
				{				
					std::cout << "DEBUG: " << it->sourceLocation() << " jumping back " << instructionsToSkip << " if " << top << '\n';
				}

				if(top.isFalsey())
				{
					it += instructionsToSkip;
				}
			}
			break;
		case Instruction::REF_LOCAL:
			handleRef(Bindings::Local, value, stack, bindings);
			if(settings_.trace)
			{
				std::cout << "DEBUG: " << it->sourceLocation() << " local ref '" << value.string() << "' is " << stack.back() << '\n';
			}
			break;
		case Instruction::INIT_LOCAL:
			{
				const Value &intialisedValue = handleInit(Bindings::Local, value, stack, bindings);
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " local init '" << value.string() << "' to " << intialisedValue << '\n';
				}
			}
			break;
		case Instruction::ASSIGN_LOCAL:
			{
				const Value &assignedValue = handleAssign(Bindings::Local, value, stack, bindings);
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " local assign '" << value.string() << "' to " << assignedValue << '\n';
				}
			}
			break;
		case Instruction::REF_GLOBAL:
			handleRef(Bindings::Global, value, stack, bindings);
			if(settings_.trace)
			{
				std::cout << "DEBUG: " << it->sourceLocation() << " global ref '" << value.string() << "' is " << stack.back() << '\n';
			}
			break;
		case Instruction::INIT_GLOBAL:
			{
				const Value &intialisedValue = handleInit(Bindings::Global, value, stack, bindings);
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " global init '" << value.string() << "' to " << intialisedValue << '\n';
				}
			}
			break;
		case Instruction::ASSIGN_GLOBAL:
			{
				const Value &assignedValue = handleAssign(Bindings::Global, value, stack, bindings);
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " global assign '" << value.string() << "' to " << assignedValue << '\n';
				}
			}
			break;
		case Instruction::REF_CLOSURE:
			handleRef(Bindings::Closure, value, stack, bindings);
			if(settings_.trace)
			{
				std::cout << "DEBUG: " << it->sourceLocation() << " closure ref '" << value.string() << "' is " << stack.back() << '\n';
			}
			break;
		case Instruction::INIT_CLOSURE:
			{
				Identifier identifier = Identifier(value.string());
				Bindings::ValuePtr &binding = bindings.getPointer(identifier);
				closureValues.push_back(ClosedNameAndValue(identifier, binding));
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " closure init '" << value.string() << "' is " << *binding << '\n';
				}
			}
			break;
		case Instruction::ASSIGN_CLOSURE:
			{
				const Value &assignedValue = handleAssign(Bindings::Closure, value, stack, bindings);
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " closure assign '" << value.string() << "' to " << assignedValue << '\n';
				}
			}
			break;
		case Instruction::MEMBER_ACCESS:
			{
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " member access " << value << '\n';
				}
				assert(value.isString());
				const std::string &memberName = value.string();

				Value top = pop(stack);
				if(!top.isObject())
				{
					throw ExecutionError(it->sourceLocation(), "Member access instruction requires an object but got " + str(top));
				}
				const Value::Object &object = top.object();
				Value::Object::const_iterator memberIterator = object.find(memberName);
				if (memberIterator == object.end())
				{
					throw ExecutionError(it->sourceLocation(), "Unknown member name " + memberName + " for " + str(top));
				}
				if(settings_.trace)
				{
					std::cout << "DEBUG: " << it->sourceLocation() << " member access " << value.string() << "." << memberName << " was " << memberIterator->second << '\n';
				}
				stack.push_back(memberIterator->second);
			}
			break;
		default:
			throw CompilerBug("unhandled instruction type: " + str(type));
		}

		if (settings_.trace)
		{
			if (stack.empty())
			{
				std::cout << "Stack is empty\n";
			}
			else
			{
				std::cout << "Stack contains " << stack.size() << " entries:\n";
				int index = 0;
				for(Stack::const_iterator it = stack.begin() ; it != stack.end() ; ++it)
				{
					++index;
					std::cout << index << ":  " << *it << '\n';
				}
			}

			if (closureValues.empty())
			{
				std::cout << "closureValues is empty\n";
			}
			else
			{
				std::cout << "closureValues contains " << closureValues.size() << " entries:\n";
				int index = 0;
				for(const ClosedNameAndValue &closedValue: closureValues)
				{
					++index;
					std::cout << index << ":  " << closedValue.first << " -> " << *closedValue.second << " @ " << closedValue.second << '\n';
				}
			}
		}
	}
	
	return stack.empty() ? Value::nil() : pop(stack);
}