<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include <xenia/kernel/xboxkrnl_debug.h>

#include <xenia/kernel/kernel_state.h>
#include <xenia/kernel/xboxkrnl_private.h>
#include <xenia/kernel/objects/xthread.h>
#include <xenia/kernel/util/shim_utils.h>

using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;

namespace xe {
namespace kernel {

// TODO: clean me up!
SHIM_CALL DbgPrint_shim(
    PPCContext* ppc_state, KernelState* state) {

  uint32_t format_ptr = SHIM_GET_ARG_32(0);
  if (format_ptr == 0) {
    SHIM_SET_RETURN(-1);
    return;
  }

  const char *format = (const char *)SHIM_MEM_ADDR(format_ptr);

  int arg_index = 0;

  char buffer[512]; // TODO: ensure it never writes past the end of the buffer...
  char *b = buffer;
  for (; *format != '\0'; ++format) {
    const char *start = format;

    if (*format != '%') {
      *b++ = *format;
      continue;
    }

    ++format;
    if (*format == '\0') {
      break;
    }

    if (*format == '%') {
      *b++ = *format;
      continue;
    }

    const char *end;
    end = format;

    // skip flags
    while (*end == '-' ||
           *end == '+' ||
           *end == ' ' ||
           *end == '#' ||
           *end == '0') {
      ++end;
    }

    if (*end == '\0') {
      break;
    }

    int arg_extras = 0;

    // skip width
    if (*end == '*') {
      ++end;
      arg_extras++;
    }
    else {
      while (*end >= '0' && *end <= '9') {
        ++end;
      }
    }

    if (*end == '\0') {
      break;
    }

    // skip precision
    if (*end == '.') {
      ++end;

      if (*end == '*') {
        ++end;
        ++arg_extras;
      }
      else {
        while (*end >= '0' && *end <= '9') {
          ++end;
        }
      }
    }

    if (*end == '\0') {
      break;
    }

    // get length
    int arg_size = 4;

    if (*end == 'h') {
      ++end;
      arg_size = 4;
      if (*end == 'h') {
        ++end;
      }
    }
    else if (*end == 'l') {
      ++end;
      arg_size = 4;
      if (*end == 'l') {
        ++end;
        arg_size = 8;
      }
    }
    else if (*end == 'j') {
      arg_size = 8;
      ++end;
    }
    else if (*end == 'z') {
      arg_size = 4;
      ++end;
    }
    else if (*end == 't') {
      arg_size = 8;
      ++end;
    }
    else if (*end == 'L') {
      arg_size = 8;
      ++end;
    }

    if (*end == '\0') {
      break;
    }

    if (*end == 'd' ||
        *end == 'i' ||
        *end == 'u' ||
        *end == 'o' ||
        *end == 'x' ||
        *end == 'X' ||
        *end == 'f' ||
        *end == 'F' ||
        *end == 'e' ||
        *end == 'E' ||
        *end == 'g' ||
        *end == 'G' ||
        *end == 'a' ||
        *end == 'A' ||
        *end == 'c') {
      char local[512];
      local[0] = '\0';
      strncat(local, start, end + 1 - start);

      XEASSERT(arg_size == 8 || arg_size == 4);
      if (arg_size == 8) {
        if (arg_extras == 0) {
          uint64_t value = arg_index < 7
            ? SHIM_GET_ARG_64(1 + arg_index)
            : SHIM_MEM_32(SHIM_GPR_32(1) + 16 + ((1 + arg_index) * 8));
          int result = sprintf(b, local, value);
          b += result;
          arg_index++;
        }
        else {
          XEASSERT(false);
        }
      }
      else if (arg_size == 4) {
        if (arg_extras == 0) {
          uint64_t value = arg_index < 7
            ? SHIM_GET_ARG_64(1 + arg_index)
            : SHIM_MEM_32(SHIM_GPR_32(1) + 16 + ((1 + arg_index) * 8));
          int result = sprintf(b, local, (uint32_t)value);
          b += result;
          arg_index++;
        }
        else {
          XEASSERT(false);
        }
      }
    }
    else if (*end == 's' ||
             *end == 'p' ||
             *end == 'n') {
      char local[512];
      local[0] = '\0';
      strncat(local, start, end + 1 - start);

      XEASSERT(arg_size == 4);
      if (arg_extras == 0) {
        uint32_t value = arg_index < 7
          ? SHIM_GET_ARG_32(1 + arg_index)
          : (uint32_t)SHIM_MEM_64(SHIM_GPR_32(1) + 16 + ((1 + arg_index) * 8));
        const char *pointer = (const char *)SHIM_MEM_ADDR(value);
        int result = sprintf(b, local, pointer);
        b += result;
        arg_index++;
      }
      else {
        XEASSERT(false);
      }
    }
    else {
      XEASSERT(false);
      break;
    }

    format = end;
  }
  *b++ = '\0';

  XELOGD("(DbgPrint) %s", buffer);
}

void xeDbgBreakPoint() {
  DebugBreak();
}

SHIM_CALL DbgBreakPoint_shim(
    PPCContext* ppc_state, KernelState* state) {
  XELOGD("DbgBreakPoint()");
}

SHIM_CALL RtlRaiseException_shim(
    PPCContext* ppc_state, KernelState* state) {
  uint32_t record_ptr = SHIM_GET_ARG_32(0);

  uint32_t code = SHIM_MEM_32(record_ptr + 0);

  XELOGD(
      "RtlRaiseException(%.8X(%.8X))",
      record_ptr, code);

  XEASSERTALWAYS();
}

}  // namespace kernel
}  // namespace xe

void xe::kernel::xboxkrnl::RegisterDebugExports(
    ExportResolver* export_resolver, KernelState* state) {
  SHIM_SET_MAPPING("xboxkrnl.exe", DbgPrint, state);
  SHIM_SET_MAPPING("xboxkrnl.exe", DbgBreakPoint, state);
  SHIM_SET_MAPPING("xboxkrnl.exe", RtlRaiseException, state);
}
<commit_msg>RtlRaiseException handling thread naming. But needs issue #54.<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include <xenia/kernel/xboxkrnl_debug.h>

#include <xenia/kernel/kernel_state.h>
#include <xenia/kernel/xboxkrnl_private.h>
#include <xenia/kernel/objects/xthread.h>
#include <xenia/kernel/util/shim_utils.h>

using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;

namespace xe {
namespace kernel {

// TODO: clean me up!
SHIM_CALL DbgPrint_shim(
    PPCContext* ppc_state, KernelState* state) {

  uint32_t format_ptr = SHIM_GET_ARG_32(0);
  if (format_ptr == 0) {
    SHIM_SET_RETURN(-1);
    return;
  }

  const char *format = (const char *)SHIM_MEM_ADDR(format_ptr);

  int arg_index = 0;

  char buffer[512]; // TODO: ensure it never writes past the end of the buffer...
  char *b = buffer;
  for (; *format != '\0'; ++format) {
    const char *start = format;

    if (*format != '%') {
      *b++ = *format;
      continue;
    }

    ++format;
    if (*format == '\0') {
      break;
    }

    if (*format == '%') {
      *b++ = *format;
      continue;
    }

    const char *end;
    end = format;

    // skip flags
    while (*end == '-' ||
           *end == '+' ||
           *end == ' ' ||
           *end == '#' ||
           *end == '0') {
      ++end;
    }

    if (*end == '\0') {
      break;
    }

    int arg_extras = 0;

    // skip width
    if (*end == '*') {
      ++end;
      arg_extras++;
    }
    else {
      while (*end >= '0' && *end <= '9') {
        ++end;
      }
    }

    if (*end == '\0') {
      break;
    }

    // skip precision
    if (*end == '.') {
      ++end;

      if (*end == '*') {
        ++end;
        ++arg_extras;
      }
      else {
        while (*end >= '0' && *end <= '9') {
          ++end;
        }
      }
    }

    if (*end == '\0') {
      break;
    }

    // get length
    int arg_size = 4;

    if (*end == 'h') {
      ++end;
      arg_size = 4;
      if (*end == 'h') {
        ++end;
      }
    }
    else if (*end == 'l') {
      ++end;
      arg_size = 4;
      if (*end == 'l') {
        ++end;
        arg_size = 8;
      }
    }
    else if (*end == 'j') {
      arg_size = 8;
      ++end;
    }
    else if (*end == 'z') {
      arg_size = 4;
      ++end;
    }
    else if (*end == 't') {
      arg_size = 8;
      ++end;
    }
    else if (*end == 'L') {
      arg_size = 8;
      ++end;
    }

    if (*end == '\0') {
      break;
    }

    if (*end == 'd' ||
        *end == 'i' ||
        *end == 'u' ||
        *end == 'o' ||
        *end == 'x' ||
        *end == 'X' ||
        *end == 'f' ||
        *end == 'F' ||
        *end == 'e' ||
        *end == 'E' ||
        *end == 'g' ||
        *end == 'G' ||
        *end == 'a' ||
        *end == 'A' ||
        *end == 'c') {
      char local[512];
      local[0] = '\0';
      strncat(local, start, end + 1 - start);

      XEASSERT(arg_size == 8 || arg_size == 4);
      if (arg_size == 8) {
        if (arg_extras == 0) {
          uint64_t value = arg_index < 7
            ? SHIM_GET_ARG_64(1 + arg_index)
            : SHIM_MEM_32(SHIM_GPR_32(1) + 16 + ((1 + arg_index) * 8));
          int result = sprintf(b, local, value);
          b += result;
          arg_index++;
        }
        else {
          XEASSERT(false);
        }
      }
      else if (arg_size == 4) {
        if (arg_extras == 0) {
          uint64_t value = arg_index < 7
            ? SHIM_GET_ARG_64(1 + arg_index)
            : SHIM_MEM_32(SHIM_GPR_32(1) + 16 + ((1 + arg_index) * 8));
          int result = sprintf(b, local, (uint32_t)value);
          b += result;
          arg_index++;
        }
        else {
          XEASSERT(false);
        }
      }
    }
    else if (*end == 's' ||
             *end == 'p' ||
             *end == 'n') {
      char local[512];
      local[0] = '\0';
      strncat(local, start, end + 1 - start);

      XEASSERT(arg_size == 4);
      if (arg_extras == 0) {
        uint32_t value = arg_index < 7
          ? SHIM_GET_ARG_32(1 + arg_index)
          : (uint32_t)SHIM_MEM_64(SHIM_GPR_32(1) + 16 + ((1 + arg_index) * 8));
        const char *pointer = (const char *)SHIM_MEM_ADDR(value);
        int result = sprintf(b, local, pointer);
        b += result;
        arg_index++;
      }
      else {
        XEASSERT(false);
      }
    }
    else {
      XEASSERT(false);
      break;
    }

    format = end;
  }
  *b++ = '\0';

  XELOGD("(DbgPrint) %s", buffer);
}

void xeDbgBreakPoint() {
  DebugBreak();
}

SHIM_CALL DbgBreakPoint_shim(
    PPCContext* ppc_state, KernelState* state) {
  XELOGD("DbgBreakPoint()");
}

SHIM_CALL RtlRaiseException_shim(
    PPCContext* ppc_state, KernelState* state) {
  uint32_t record_ptr = SHIM_GET_ARG_32(0);

  uint32_t code = SHIM_MEM_32(record_ptr + 0);
  uint32_t flags = SHIM_MEM_32(record_ptr + 4);
  // ...
  uint32_t param_count = SHIM_MEM_32(record_ptr + 16);

  XELOGD(
      "RtlRaiseException(%.8X(%.8X))",
      record_ptr, code);

  if (code == 0x406D1388) {
    // SetThreadName. FFS.
    uint32_t thread_info_ptr = record_ptr + 20;
    uint32_t type = SHIM_MEM_32(thread_info_ptr + 0);
    XEASSERT(type == 0x1000);
    uint32_t name_ptr = SHIM_MEM_32(thread_info_ptr + 4);
    uint32_t thread_id = SHIM_MEM_32(thread_info_ptr + 8);

    const char* name = (const char*)SHIM_MEM_ADDR(name_ptr);

    XThread* thread = NULL;
    if (thread_id == -1) {
      // Current thread.
      thread = XThread::GetCurrentThread();
      thread->Retain();
    } else {
      // Lookup thread by ID.
      thread = state->GetThreadByID(thread_id);
    }

    if (thread) {
      thread->set_name(name);
      thread->Release();
    }
  }

  // TODO(benvanik): unwinding.
  // This is going to suck.
  XEASSERTALWAYS();
}

}  // namespace kernel
}  // namespace xe

void xe::kernel::xboxkrnl::RegisterDebugExports(
    ExportResolver* export_resolver, KernelState* state) {
  SHIM_SET_MAPPING("xboxkrnl.exe", DbgPrint, state);
  SHIM_SET_MAPPING("xboxkrnl.exe", DbgBreakPoint, state);
  SHIM_SET_MAPPING("xboxkrnl.exe", RtlRaiseException, state);
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2009 Sony Pictures Imageworks, et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/////////////////////////////////////////////////////////////////////////
/// \file
///
/// Shader interpreter implementation of control flow statements
/// such as 'if', 'for', etc.
///
/////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "oslexec_pvt.h"
#include "oslops.h"

#include "OpenImageIO/varyingref.h"
#include "OpenImageIO/sysutil.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif
namespace OSL {
namespace pvt {

DECLOP (OP_if)
{
    ASSERT (nargs == 1);
    Symbol &Condition (exec->sym (args[0]));
    ASSERT (Condition.typespec().is_int());
    VaryingRef<int> condition ((int *)Condition.data(), Condition.step());
    Opcode &op (exec->op());

    // Determine if it's a "uniform if"
    bool uniform = Condition.is_uniform ();
    if (! uniform) {
        // Second chance -- what if the condition is varying, but the
        // results are the same at all points?
        uniform = true;
        for (int i = beginpoint+1;  i < endpoint;  ++i)
            if (runflags[i] && condition[i] != condition[beginpoint]) {
                uniform = false;
                break;
            }
    }

    // FIXME -- if there's potentially a 'break' or 'continue' inside
    // this conditional, we need to treat it as varying.

    if (uniform) {
        // Uniform condition -- don't need new runflags
        if (condition[beginpoint]) {
            // Condition is true -- execute the true clause.
            // But if there is no else clause, do nothing (!) and the
            // normal execution will just take care of itself
            if (op.jump(1) != op.jump(0)) {
                exec->run (exec->ip()+1, op.jump(0));  // Run the true clause
                exec->ip (op.jump(1) - 1);             // Skip the false clause
            }
        } else {
            // Condition is false -- just a jump to 'else' and keep going
            exec->ip (op.jump(0) - 1);
        }
        return;
    }

    // From here on, varying condition or potential break/continue at play

    // Generate new true and false runflags based on the condition
    Runflag *true_runflags = ALLOCA (Runflag, exec->npoints());
    memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
    Runflag *false_runflags = ALLOCA (Runflag, exec->npoints());
    memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
    for (int i = beginpoint;  i < endpoint;  ++i) {
        if (runflags[i]) {
            if (condition[i])
                false_runflags[i] = RunflagOff;
            else
                true_runflags[i] = RunflagOff;
        }
    }

    // True clause
    exec->push_runflags (true_runflags, beginpoint, endpoint);
    exec->run (exec->ip() + 1, op.jump(0));
    exec->pop_runflags ();

    // False clause
    if (op.jump(0) < op.jump(1)) {
        exec->push_runflags (false_runflags, beginpoint, endpoint);
        exec->run (op.jump(0), op.jump(1));
        exec->pop_runflags ();
    }

    // Jump to after the if (remember that the interpreter loop will
    // increment the ip one more time, so back up one.
    exec->ip (op.jump(1) - 1);

    // FIXME -- we may need to call new_runflag_range here if, during
    // execution, we may have hit a 'break' or 'continue'.
}

DECLOP (OP_for)
{
    ASSERT (nargs == 1);
    Symbol &Condition (exec->sym (args[0]));
    ASSERT (Condition.typespec().is_int());
    Opcode &op (exec->op());

    // Jump addresses
    int startinit = exec->ip() + 1;
    int startcondition = op.jump (0);
    int startbody = op.jump (1);
    int startiterate = op.jump (2);
    int done = op.jump (3);

    // Execute the initialization
    if (startinit < startcondition)
        exec->run (startinit, startcondition);

    Runflag *true_runflags = NULL;  // Allocate as needed
    while (1) {
        // Execute the condition
        exec->run (startcondition, startbody);

        // Determine if it's a "uniform if"
        bool uniform = Condition.is_uniform ();
        VaryingRef<int> condition ((int *)Condition.data(), Condition.step());

        // FIXME -- if there's potentially a 'break' or 'continue' inside
        // this loop, we need to treat it as varying.

        if (uniform) {
            // Uniform condition -- don't need new runflags
            if (condition[beginpoint])
                exec->run (startbody, startiterate);  // Run the body
            else
                break;   // break out of the loop
        } else {
            // From here on, varying condition or potential
            // break/continue at play

            // Generate new runflags based on the condition
            if (! true_runflags) {
                true_runflags = ALLOCA (Runflag, exec->npoints());
                memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            int turnedoff = 0;  // Number of points that turned off
            bool all_off = true;  // Are all points turned off?
            for (int i = beginpoint;  i < endpoint;  ++i) {
                if (true_runflags[i]) {
                    if (condition[i])
                        all_off = false;  // this point is still on
                    else {
                        // this point has turned off on this iteration
                        true_runflags[i] = RunflagOff;
                        ++turnedoff;
                    }
                }
            }
            if (all_off)
                break;     // No points left on

            // At least one point is still on
            if (turnedoff) {
                // If we turned off any "new" points on this iteration,
                // reset the runflags
                exec->pop_runflags ();
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            // Execute the body
            exec->run (startbody, startiterate);

            // FIXME -- we may need to call new_runflag_range here if, during
            // execution, we may have hit a 'break' or 'continue'.
        }

        if (startiterate < done)
            exec->run (startiterate, done);
    }

    if (true_runflags) {
        // Restore old runflags if we ever made new ones
        exec->pop_runflags ();
    }

    // Skip to after the loop
    exec->ip (done-1);
}

DECLOP (OP_dowhile)
{
    ASSERT (nargs == 1);
    Symbol &Condition (exec->sym (args[0]));
    ASSERT (Condition.typespec().is_int());
    Opcode &op (exec->op());

    // Jump addresses
    int startinit = exec->ip() + 1;
    int startcondition = op.jump (0);
    int startbody = op.jump (1);
    int startiterate = op.jump (2);
    int done = op.jump (3);

    // Execute the initialization
    if (startinit < startcondition)
        exec->run (startinit, startcondition);

    Runflag *true_runflags = NULL;  // Allocate as needed
    while (1) {
        // Execute the body
        exec->run (startbody, startiterate);

        // Execute the condition
        exec->run (startcondition, startbody);

        // Determine if it's a "uniform if"
        bool uniform = Condition.is_uniform ();
        VaryingRef<int> condition ((int *)Condition.data(), Condition.step());

        // FIXME -- if there's potentially a 'break' or 'continue' inside
        // this loop, we need to treat it as varying.

        if (uniform) {
            // Uniform condition -- don't need new runflags
            if (condition[beginpoint])
                continue;    // All true, back to the beginning
            else
                break;       // All false, break out of the loop
        } else {
            // From here on, varying condition or potential
            // break/continue at play

            // Generate new runflags based on the condition
            if (! true_runflags) {
                true_runflags = ALLOCA (Runflag, exec->npoints());
                memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            int turnedoff = 0;  // Number of points that turned off
            bool all_off = true;  // Are all points turned off?
            for (int i = beginpoint;  i < endpoint;  ++i) {
                if (true_runflags[i]) {
                    if (condition[i])
                        all_off = false;  // this point is still on
                    else {
                        // this point has turned off on this iteration
                        true_runflags[i] = RunflagOff;
                        ++turnedoff;
                    }
                }
            }
            if (all_off)
                break;     // No points left on

            // At least one point is still on
            if (turnedoff) {
                // If we turned off any "new" points on this iteration,
                // reset the runflags
                exec->pop_runflags ();
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            // FIXME -- we may need to call new_runflag_range here if, during
            // execution, we may have hit a 'break' or 'continue'.
        }
    }

    if (true_runflags) {
        // Restore old runflags if we ever made new ones
        exec->pop_runflags ();
    }

    // Skip to after the loop
    exec->ip (done-1);
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<commit_msg>Bug fix -- wasn't initializing the 'false' case runflags for varying 'if'.<commit_after>/*
Copyright (c) 2009 Sony Pictures Imageworks, et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/////////////////////////////////////////////////////////////////////////
/// \file
///
/// Shader interpreter implementation of control flow statements
/// such as 'if', 'for', etc.
///
/////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "oslexec_pvt.h"
#include "oslops.h"

#include "OpenImageIO/varyingref.h"
#include "OpenImageIO/sysutil.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif
namespace OSL {
namespace pvt {

DECLOP (OP_if)
{
    ASSERT (nargs == 1);
    Symbol &Condition (exec->sym (args[0]));
    ASSERT (Condition.typespec().is_int());
    VaryingRef<int> condition ((int *)Condition.data(), Condition.step());
    Opcode &op (exec->op());

    // Determine if it's a "uniform if"
    bool uniform = Condition.is_uniform ();
    if (! uniform) {
        // Second chance -- what if the condition is varying, but the
        // results are the same at all points?
        uniform = true;
        for (int i = beginpoint+1;  i < endpoint;  ++i)
            if (runflags[i] && condition[i] != condition[beginpoint]) {
                uniform = false;
                break;
            }
    }

    // FIXME -- if there's potentially a 'break' or 'continue' inside
    // this conditional, we need to treat it as varying.

    if (uniform) {
        // Uniform condition -- don't need new runflags
        if (condition[beginpoint]) {
            // Condition is true -- execute the true clause.
            // But if there is no else clause, do nothing (!) and the
            // normal execution will just take care of itself
            if (op.jump(1) != op.jump(0)) {
                exec->run (exec->ip()+1, op.jump(0));  // Run the true clause
                exec->ip (op.jump(1) - 1);             // Skip the false clause
            }
        } else {
            // Condition is false -- just a jump to 'else' and keep going
            exec->ip (op.jump(0) - 1);
        }
        return;
    }

    // From here on, varying condition or potential break/continue at play

    // Generate new true and false runflags based on the condition
    Runflag *true_runflags = ALLOCA (Runflag, exec->npoints());
    memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
    Runflag *false_runflags = ALLOCA (Runflag, exec->npoints());
    memcpy (false_runflags, runflags, exec->npoints() * sizeof(Runflag));
    for (int i = beginpoint;  i < endpoint;  ++i) {
        if (runflags[i]) {
            if (condition[i])
                false_runflags[i] = RunflagOff;
            else
                true_runflags[i] = RunflagOff;
        }
    }

    // True clause
    exec->push_runflags (true_runflags, beginpoint, endpoint);
    exec->run (exec->ip() + 1, op.jump(0));
    exec->pop_runflags ();

    // False clause
    if (op.jump(0) < op.jump(1)) {
        exec->push_runflags (false_runflags, beginpoint, endpoint);
        exec->run (op.jump(0), op.jump(1));
        exec->pop_runflags ();
    }

    // Jump to after the if (remember that the interpreter loop will
    // increment the ip one more time, so back up one.
    exec->ip (op.jump(1) - 1);

    // FIXME -- we may need to call new_runflag_range here if, during
    // execution, we may have hit a 'break' or 'continue'.
}

DECLOP (OP_for)
{
    ASSERT (nargs == 1);
    Symbol &Condition (exec->sym (args[0]));
    ASSERT (Condition.typespec().is_int());
    Opcode &op (exec->op());

    // Jump addresses
    int startinit = exec->ip() + 1;
    int startcondition = op.jump (0);
    int startbody = op.jump (1);
    int startiterate = op.jump (2);
    int done = op.jump (3);

    // Execute the initialization
    if (startinit < startcondition)
        exec->run (startinit, startcondition);

    Runflag *true_runflags = NULL;  // Allocate as needed
    while (1) {
        // Execute the condition
        exec->run (startcondition, startbody);

        // Determine if it's a "uniform if"
        bool uniform = Condition.is_uniform ();
        VaryingRef<int> condition ((int *)Condition.data(), Condition.step());

        // FIXME -- if there's potentially a 'break' or 'continue' inside
        // this loop, we need to treat it as varying.

        if (uniform) {
            // Uniform condition -- don't need new runflags
            if (condition[beginpoint])
                exec->run (startbody, startiterate);  // Run the body
            else
                break;   // break out of the loop
        } else {
            // From here on, varying condition or potential
            // break/continue at play

            // Generate new runflags based on the condition
            if (! true_runflags) {
                true_runflags = ALLOCA (Runflag, exec->npoints());
                memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            int turnedoff = 0;  // Number of points that turned off
            bool all_off = true;  // Are all points turned off?
            for (int i = beginpoint;  i < endpoint;  ++i) {
                if (true_runflags[i]) {
                    if (condition[i])
                        all_off = false;  // this point is still on
                    else {
                        // this point has turned off on this iteration
                        true_runflags[i] = RunflagOff;
                        ++turnedoff;
                    }
                }
            }
            if (all_off)
                break;     // No points left on

            // At least one point is still on
            if (turnedoff) {
                // If we turned off any "new" points on this iteration,
                // reset the runflags
                exec->pop_runflags ();
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            // Execute the body
            exec->run (startbody, startiterate);

            // FIXME -- we may need to call new_runflag_range here if, during
            // execution, we may have hit a 'break' or 'continue'.
        }

        if (startiterate < done)
            exec->run (startiterate, done);
    }

    if (true_runflags) {
        // Restore old runflags if we ever made new ones
        exec->pop_runflags ();
    }

    // Skip to after the loop
    exec->ip (done-1);
}

DECLOP (OP_dowhile)
{
    ASSERT (nargs == 1);
    Symbol &Condition (exec->sym (args[0]));
    ASSERT (Condition.typespec().is_int());
    Opcode &op (exec->op());

    // Jump addresses
    int startinit = exec->ip() + 1;
    int startcondition = op.jump (0);
    int startbody = op.jump (1);
    int startiterate = op.jump (2);
    int done = op.jump (3);

    // Execute the initialization
    if (startinit < startcondition)
        exec->run (startinit, startcondition);

    Runflag *true_runflags = NULL;  // Allocate as needed
    while (1) {
        // Execute the body
        exec->run (startbody, startiterate);

        // Execute the condition
        exec->run (startcondition, startbody);

        // Determine if it's a "uniform if"
        bool uniform = Condition.is_uniform ();
        VaryingRef<int> condition ((int *)Condition.data(), Condition.step());

        // FIXME -- if there's potentially a 'break' or 'continue' inside
        // this loop, we need to treat it as varying.

        if (uniform) {
            // Uniform condition -- don't need new runflags
            if (condition[beginpoint])
                continue;    // All true, back to the beginning
            else
                break;       // All false, break out of the loop
        } else {
            // From here on, varying condition or potential
            // break/continue at play

            // Generate new runflags based on the condition
            if (! true_runflags) {
                true_runflags = ALLOCA (Runflag, exec->npoints());
                memcpy (true_runflags, runflags, exec->npoints() * sizeof(Runflag));
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            int turnedoff = 0;  // Number of points that turned off
            bool all_off = true;  // Are all points turned off?
            for (int i = beginpoint;  i < endpoint;  ++i) {
                if (true_runflags[i]) {
                    if (condition[i])
                        all_off = false;  // this point is still on
                    else {
                        // this point has turned off on this iteration
                        true_runflags[i] = RunflagOff;
                        ++turnedoff;
                    }
                }
            }
            if (all_off)
                break;     // No points left on

            // At least one point is still on
            if (turnedoff) {
                // If we turned off any "new" points on this iteration,
                // reset the runflags
                exec->pop_runflags ();
                exec->push_runflags (true_runflags, beginpoint, endpoint);
            }
            // FIXME -- we may need to call new_runflag_range here if, during
            // execution, we may have hit a 'break' or 'continue'.
        }
    }

    if (true_runflags) {
        // Restore old runflags if we ever made new ones
        exec->pop_runflags ();
    }

    // Skip to after the loop
    exec->ip (done-1);
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<|endoftext|>


<commit_before>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// Qt includes
#include <QApplication>
#include <QDebug>
#include <QIcon>
#include <QMouseEvent>
#include <QPainter>
#include <QRect>
#include <QStyleOption>

// CTK includes
#include "ctkSearchBox.h"

// --------------------------------------------------
class ctkSearchBoxPrivate
{
  Q_DECLARE_PUBLIC(ctkSearchBox);
protected:
  ctkSearchBox* const q_ptr;
public:
  ctkSearchBoxPrivate(ctkSearchBox& object);
  void init();

  /// Position and size for the clear icon in the QLineEdit
  QRect clearRect()const;
  /// Position and size for the search icon in the QLineEdit
  QRect searchRect()const;

  QIcon clearIcon;
  QIcon searchIcon;

  QIcon::Mode clearIconMode;
};

// --------------------------------------------------
ctkSearchBoxPrivate::ctkSearchBoxPrivate(ctkSearchBox &object)
  : q_ptr(&object)
{
  this->clearIcon = QIcon(":Icons/clear.svg");
  this->searchIcon = QIcon(":Icons/search.svg");
  this->clearIconMode = QIcon::Disabled;
}

// --------------------------------------------------
void ctkSearchBoxPrivate::init()
{
  Q_Q(ctkSearchBox);

  // Set a text by default on the QLineEdit
  q->setPlaceholderText(q->tr("Search..."));

  QObject::connect(q, SIGNAL(textChanged(const QString&)),
                   q, SLOT(updateClearButtonState()));
}

// --------------------------------------------------
QRect ctkSearchBoxPrivate::clearRect()const
{
  Q_Q(const ctkSearchBox);
  QRect cRect = this->searchRect();
  cRect.moveLeft(q->width() - cRect.width() - cRect.left());
  return cRect;
}

// --------------------------------------------------
QRect ctkSearchBoxPrivate::searchRect()const
{
  Q_Q(const ctkSearchBox);
  QRect sRect;
  // If the QLineEdit has a frame, the icon must be shifted from
  // the frame line width
  if (q->hasFrame())
    {
    QStyleOptionFrameV2 opt;
    q->initStyleOption(&opt);
    sRect.moveTopLeft(QPoint(opt.lineWidth, opt.lineWidth));
    }
  // Hardcoded: shift by 1 pixel because some styles have a focus frame inside
  // the line edit frame.
  sRect.translate(QPoint(1,1));
  // Square size
  sRect.setSize(QSize(q->height(),q->height()) -
                2*QSize(sRect.left(), sRect.top()));
  return sRect;
}

// --------------------------------------------------
ctkSearchBox::ctkSearchBox(QWidget* _parent)
  : QLineEdit(_parent)
  , d_ptr(new ctkSearchBoxPrivate(*this))
{
  Q_D(ctkSearchBox);
  d->init();
}

// --------------------------------------------------
ctkSearchBox::~ctkSearchBox()
{
}

// --------------------------------------------------
void ctkSearchBox::paintEvent(QPaintEvent * event)
{
  Q_D(ctkSearchBox);
  QPainter p(this);

  // Draw the line edit with text.
  // Text has already been shifted to the right (in resizeEvent()) to leave
  // space for the search icon.
  this->Superclass::paintEvent(event);

  // Draw clearIcon
  QRect cRect = d->clearRect();
  QPixmap closePixmap = d->clearIcon.pixmap(cRect.size(),d->clearIconMode);
  this->style()->drawItemPixmap(&p, cRect, Qt::AlignCenter, closePixmap);

  // Draw searchIcon
  QRect sRect = d->searchRect();
  QPixmap searchPixmap = d->searchIcon.pixmap(sRect.size());
  this->style()->drawItemPixmap(&p, sRect, Qt::AlignCenter, searchPixmap);

}

// --------------------------------------------------
void ctkSearchBox::mousePressEvent(QMouseEvent *e)
{
  Q_D(ctkSearchBox);

  if(d->clearRect().contains(e->pos()))
    {
    this->clear();
    return;
    }

  if(d->searchRect().contains(e->pos()))
    {
    this->selectAll();
    return;
    }

  this->Superclass::mousePressEvent(e);
}

// --------------------------------------------------
void ctkSearchBox::mouseMoveEvent(QMouseEvent *e)
{
  Q_D(ctkSearchBox);

  if(d->clearRect().contains(e->pos()) || d->searchRect().contains(e->pos()))
    {
    this->setCursor(Qt::ArrowCursor);
    }
  else
    {
    this->setCursor(this->isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
    }
  this->Superclass::mouseMoveEvent(e);
}

// --------------------------------------------------
void ctkSearchBox::resizeEvent(QResizeEvent * event)
{
  Q_D(ctkSearchBox);
  static int iconSpacing = 4; // hardcoded, same way as pushbutton icon spacing
  QRect cRect = d->clearRect();
  QRect sRect = d->searchRect();
  // Set 2 margins each sides of the QLineEdit, according to the icons
  this->setTextMargins( sRect.right() + iconSpacing, 0,
                        event->size().width() - cRect.left() - iconSpacing,0);
}

// --------------------------------------------------
void ctkSearchBox::updateClearButtonState()
{
  Q_D(ctkSearchBox);
  d->clearIconMode = this->text().isEmpty() ? QIcon::Disabled : QIcon::Normal;
}

<commit_msg>Modification to fix a probleme under Windows<commit_after>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=========================================================================*/

// Qt includes
#include <QApplication>
#include <QDebug>
#include <QIcon>
#include <QMouseEvent>
#include <QPainter>
#include <QRect>
#include <QStyleOption>

// CTK includes
#include "ctkSearchBox.h"

// --------------------------------------------------
class ctkSearchBoxPrivate
{
  Q_DECLARE_PUBLIC(ctkSearchBox);
protected:
  ctkSearchBox* const q_ptr;
public:
  ctkSearchBoxPrivate(ctkSearchBox& object);
  void init();

  /// Position and size for the clear icon in the QLineEdit
  QRect clearRect()const;
  /// Position and size for the search icon in the QLineEdit
  QRect searchRect()const;

  QIcon clearIcon;
  QIcon searchIcon;

  QIcon::Mode clearIconMode;
};

// --------------------------------------------------
ctkSearchBoxPrivate::ctkSearchBoxPrivate(ctkSearchBox &object)
  : q_ptr(&object)
{
  this->clearIcon = QIcon(":Icons/clear.svg");
  this->searchIcon = QIcon(":Icons/search.svg");
  this->clearIconMode = QIcon::Disabled;
}

// --------------------------------------------------
void ctkSearchBoxPrivate::init()
{
  Q_Q(ctkSearchBox);

  // Set a text by default on the QLineEdit
  q->setPlaceholderText(q->tr("Search..."));

  QObject::connect(q, SIGNAL(textChanged(const QString&)),
                   q, SLOT(updateClearButtonState()));
}

// --------------------------------------------------
QRect ctkSearchBoxPrivate::clearRect()const
{
  Q_Q(const ctkSearchBox);
  QRect cRect = this->searchRect();
  cRect.moveLeft(q->width() - cRect.width() - cRect.left());
  return cRect;
}

// --------------------------------------------------
QRect ctkSearchBoxPrivate::searchRect()const
{
  Q_Q(const ctkSearchBox);
  QRect sRect;
  // If the QLineEdit has a frame, the icon must be shifted from
  // the frame line width
  if (q->hasFrame())
    {
    QStyleOptionFrameV2 opt;
    q->initStyleOption(&opt);
    sRect.moveTopLeft(QPoint(opt.lineWidth, opt.lineWidth));
    }
  // Hardcoded: shift by 1 pixel because some styles have a focus frame inside
  // the line edit frame.
  sRect.translate(QPoint(1,1));
  // Square size
  sRect.setSize(QSize(q->height(),q->height()) -
                2*QSize(sRect.left(), sRect.top()));
  return sRect;
}

// --------------------------------------------------
ctkSearchBox::ctkSearchBox(QWidget* _parent)
  : QLineEdit(_parent)
  , d_ptr(new ctkSearchBoxPrivate(*this))
{
  Q_D(ctkSearchBox);
  d->init();
}

// --------------------------------------------------
ctkSearchBox::~ctkSearchBox()
{
}

// --------------------------------------------------
void ctkSearchBox::paintEvent(QPaintEvent * event)
{
  Q_D(ctkSearchBox);

  // Draw the line edit with text.
  // Text has already been shifted to the right (in resizeEvent()) to leave
  // space for the search icon.
  this->Superclass::paintEvent(event);

  QPainter p(this);

  // Draw clearIcon
  QRect cRect = d->clearRect();
  QPixmap closePixmap = d->clearIcon.pixmap(cRect.size(),d->clearIconMode);
  this->style()->drawItemPixmap(&p, cRect, Qt::AlignCenter, closePixmap);

  // Draw searchIcon
  QRect sRect = d->searchRect();
  QPixmap searchPixmap = d->searchIcon.pixmap(sRect.size());
  this->style()->drawItemPixmap(&p, sRect, Qt::AlignCenter, searchPixmap);

}

// --------------------------------------------------
void ctkSearchBox::mousePressEvent(QMouseEvent *e)
{
  Q_D(ctkSearchBox);

  if(d->clearRect().contains(e->pos()))
    {
    this->clear();
    return;
    }

  if(d->searchRect().contains(e->pos()))
    {
    this->selectAll();
    return;
    }

  this->Superclass::mousePressEvent(e);
}

// --------------------------------------------------
void ctkSearchBox::mouseMoveEvent(QMouseEvent *e)
{
  Q_D(ctkSearchBox);

  if(d->clearRect().contains(e->pos()) || d->searchRect().contains(e->pos()))
    {
    this->setCursor(Qt::ArrowCursor);
    }
  else
    {
    this->setCursor(this->isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
    }
  this->Superclass::mouseMoveEvent(e);
}

// --------------------------------------------------
void ctkSearchBox::resizeEvent(QResizeEvent * event)
{
  Q_D(ctkSearchBox);
  static int iconSpacing = 4; // hardcoded, same way as pushbutton icon spacing
  QRect cRect = d->clearRect();
  QRect sRect = d->searchRect();
  // Set 2 margins each sides of the QLineEdit, according to the icons
  this->setTextMargins( sRect.right() + iconSpacing, 0,
                        event->size().width() - cRect.left() - iconSpacing,0);
}

// --------------------------------------------------
void ctkSearchBox::updateClearButtonState()
{
  Q_D(ctkSearchBox);
  d->clearIconMode = this->text().isEmpty() ? QIcon::Disabled : QIcon::Normal;
}

<|endoftext|>


<commit_before>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "util/flet.h"
#include "util/scoped_map.h"
#include "util/interrupt.h"
#include "kernel/environment.h"
#include "kernel/normalizer.h"
#include "kernel/builtin.h"
#include "kernel/kernel_exception.h"
#include "kernel/type_checker_justification.h"
#include "kernel/instantiate.h"
#include "kernel/free_vars.h"
#include "kernel/metavar.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"

namespace lean {
static name g_x_name("x");
class type_inferer::imp {
    typedef scoped_map<expr, expr, expr_hash_alloc, expr_eqp> cache;
    typedef buffer<unification_constraint> unification_constraints;

    environment               m_env;
    context                   m_ctx;
    metavar_env *             m_menv;
    unsigned                  m_menv_timestamp;
    unification_constraints * m_uc;
    normalizer                m_normalizer;
    cache                     m_cache;

    expr normalize(expr const & e, context const & ctx) {
        return m_normalizer(e, ctx);
    }

    expr check_type(expr const & e, expr const & s, context const & ctx) {
        if (is_type(e))
            return e;
        if (is_bool(e))
            return Type();
        expr u = normalize(e, ctx);
        if (is_type(u))
            return u;
        if (is_bool(u))
            return Type();
        if (has_metavar(u) && m_menv) {
            justification jst = mk_type_expected_justification(ctx, s);
            m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
            return u;
        }
        throw type_expected_exception(m_env, ctx, s);
    }

    expr get_range(expr t, expr const & e, context const & ctx) {
        unsigned num = num_args(e) - 1;
        while (num > 0) {
            --num;
            if (is_pi(t)) {
                t = abst_body(t);
            } else {
                t = m_normalizer(t, ctx);
                if (is_pi(t)) {
                    t = abst_body(t);
                } else if (has_metavar(t) && m_menv) {
                    // Create two fresh variables A and B,
                    // and assign r == (Pi(x : A), B x)
                    expr A   = m_menv->mk_metavar(ctx);
                    expr B   = m_menv->mk_metavar(ctx);
                    expr p   = mk_pi(g_x_name, A, B(Var(0)));
                    justification jst = mk_function_expected_justification(ctx, e);
                    m_uc->push_back(mk_eq_constraint(ctx, t, p, jst));
                    t        = abst_body(p);
                } else {
                    throw function_expected_exception(m_env, ctx, e);
                }
            }
        }
        if (closed(t))
            return t;
        else
            return instantiate(t, num_args(e)-1, &arg(e, 1));
    }

    void set_menv(metavar_env * menv) {
        if (m_menv == menv) {
            // Check whether m_menv has been updated since the last time the checker has been invoked
            if (m_menv && m_menv->get_timestamp() > m_menv_timestamp) {
                m_menv_timestamp = m_menv->get_timestamp();
                m_cache.clear();
            }
        } else {
            m_menv = menv;
            m_cache.clear();
            m_menv_timestamp = m_menv ? m_menv->get_timestamp() : 0;
        }
    }

    expr infer_type(expr const & e, context const & ctx) {
        // cheap cases, we do not cache results
        switch (e.kind()) {
        case expr_kind::MetaVar:
            if (m_menv) {
                if (m_menv->is_assigned(e))
                    return infer_type(m_menv->get_subst(e), ctx);
                else
                    return m_menv->get_type(e);
            } else {
                throw unexpected_metavar_occurrence(m_env, e);
            }
        case expr_kind::Constant: {
            if (const_type(e)) {
                return const_type(e);
            } else {
                object const & obj = m_env.get_object(const_name(e));
                if (obj.has_type())
                    return obj.get_type();
                else
                    throw has_no_type_exception(m_env, e);
            }
            break;
        }
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            if (ce.get_domain()) {
                context const & ce_ctx   = p.second;
                return lift_free_vars(ce.get_domain(), ctx.size() - ce_ctx.size());
            }
            // Remark: the case where ce.get_domain() is not
            // available is not considered cheap.
            break;
        }
        case expr_kind::Eq:
            return mk_bool_type();
        case expr_kind::Value:
            return to_value(e).get_type();
        case expr_kind::Type:
            return mk_type(ty_level(e) + 1);
        case expr_kind::App: case expr_kind::Lambda:
        case expr_kind::Pi:  case expr_kind::Let:
            break; // expensive cases
        }

        check_interrupted();
        bool shared = false;
        if (is_shared(e)) {
            shared = true;
            auto it = m_cache.find(e);
            if (it != m_cache.end())
                return it->second;
        }

        expr r;
        switch (e.kind()) {
        case expr_kind::Constant: case expr_kind::Eq:
        case expr_kind::Value:    case expr_kind::Type:
        case expr_kind::MetaVar:
            lean_unreachable(); // LCOV_EXCL_LINE
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            context const & ce_ctx   = p.second;
            lean_assert(!ce.get_domain());
            r = lift_free_vars(infer_type(ce.get_body(), ce_ctx), ctx.size() - ce_ctx.size());
            break;
        }
        case expr_kind::App: {
            expr const & f = arg(e, 0);
            expr f_t = infer_type(f, ctx);
            r = get_range(f_t, e, ctx);
            break;
        }
        case expr_kind::Lambda: {
            cache::mk_scope sc(m_cache);
            r = mk_pi(abst_name(e), abst_domain(e), infer_type(abst_body(e), extend(ctx, abst_name(e), abst_domain(e))));
            break;
        }
        case expr_kind::Pi: {
            expr t1  = check_type(infer_type(abst_domain(e), ctx), abst_domain(e), ctx);
            expr t2;
            {
                cache::mk_scope sc(m_cache);
                context new_ctx = extend(ctx, abst_name(e), abst_domain(e));
                t2 = check_type(infer_type(abst_body(e), new_ctx), abst_body(e), new_ctx);
            }
            if (is_type(t1) && is_type(t2)) {
                r = mk_type(max(ty_level(t1), ty_level(t2)));
            } else {
                lean_assert(m_uc);
                justification jst = mk_max_type_justification(ctx, e);
                r = m_menv->mk_metavar(ctx);
                m_uc->push_back(mk_max_constraint(ctx, t1, t2, r, jst));
            }
            break;
        }
        case expr_kind::Let: {
            cache::mk_scope sc(m_cache);
            r = infer_type(let_body(e), extend(ctx, let_name(e), let_type(e), let_value(e)));
            break;
        }}

        if (shared) {
            m_cache.insert(e, r);
        }
        return r;
    }

    void set_ctx(context const & ctx) {
        if (!is_eqp(m_ctx, ctx)) {
            clear();
            m_ctx = ctx;
        }
    }

public:
    imp(environment const & env):
        m_env(env),
        m_normalizer(env) {
        m_menv           = nullptr;
        m_menv_timestamp = 0;
        m_uc             = nullptr;
    }

    expr operator()(expr const & e, context const & ctx, metavar_env * menv, buffer<unification_constraint> & uc) {
        set_ctx(ctx);
        set_menv(menv);
        flet<unification_constraints*> set(m_uc, &uc);
        return infer_type(e, ctx);
    }

    void clear() {
        m_cache.clear();
        m_normalizer.clear();
        m_ctx            = context();
        m_menv           = nullptr;
        m_menv_timestamp = 0;
    }

    bool is_proposition(expr const & e, context const & ctx) {
        buffer<unification_constraint> uc;
        expr t = operator()(e, ctx, nullptr, uc);
        if (is_bool(t))
            return true;
        else
            return is_bool(normalize(e, ctx));
    }
};
type_inferer::type_inferer(environment const & env):m_ptr(new imp(env)) {}
type_inferer::~type_inferer() {}
expr type_inferer::operator()(expr const & e, context const & ctx, metavar_env * menv, buffer<unification_constraint> & uc) {
    return m_ptr->operator()(e, ctx, menv, uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx) {
    buffer<unification_constraint> uc;
    return operator()(e, ctx, nullptr, uc);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx) {
    return m_ptr->is_proposition(e, ctx);
}
void type_inferer::clear() { m_ptr->clear(); }

constexpr char const * type_inferer_mt = "type_inferer";
type_inferer & to_type_inferer(lua_State * L, int i) { return *static_cast<type_inferer*>(luaL_checkudata(L, i, type_inferer_mt)); }
DECL_PRED(type_inferer)
DECL_GC(type_inferer)

static int type_inferer_call(lua_State * L) {
    int nargs = lua_gettop(L);
    type_inferer & inferer = to_type_inferer(L, 1);
    if (nargs == 2)
        return push_expr(L, inferer(to_nonnull_expr(L, 2)));
    else
        return push_expr(L, inferer(to_nonnull_expr(L, 2), to_context(L, 3)));
}

static int type_inferer_clear(lua_State * L) {
    to_type_inferer(L, 1).clear();
    return 0;
}

static int mk_type_inferer(lua_State * L) {
    void * mem = lua_newuserdata(L, sizeof(type_inferer));
    new (mem) type_inferer(to_environment(L, 1));
    luaL_getmetatable(L, type_inferer_mt);
    lua_setmetatable(L, -2);
    return 1;
}

static const struct luaL_Reg type_inferer_m[] = {
    {"__gc",            type_inferer_gc}, // never throws
    {"__call",          safe_function<type_inferer_call>},
    {"clear",           safe_function<type_inferer_clear>},
    {0, 0}
};

void open_type_inferer(lua_State * L) {
    luaL_newmetatable(L, type_inferer_mt);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    setfuncs(L, type_inferer_m, 0);

    SET_GLOBAL_FUN(mk_type_inferer,          "type_inferer");
    SET_GLOBAL_FUN(type_inferer_pred,        "is_type_inferer");
}
}
<commit_msg>fix(library/type_inferer): typo<commit_after>/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "util/flet.h"
#include "util/scoped_map.h"
#include "util/interrupt.h"
#include "kernel/environment.h"
#include "kernel/normalizer.h"
#include "kernel/builtin.h"
#include "kernel/kernel_exception.h"
#include "kernel/type_checker_justification.h"
#include "kernel/instantiate.h"
#include "kernel/free_vars.h"
#include "kernel/metavar.h"
#include "library/kernel_bindings.h"
#include "library/type_inferer.h"

namespace lean {
static name g_x_name("x");
class type_inferer::imp {
    typedef scoped_map<expr, expr, expr_hash_alloc, expr_eqp> cache;
    typedef buffer<unification_constraint> unification_constraints;

    environment               m_env;
    context                   m_ctx;
    metavar_env *             m_menv;
    unsigned                  m_menv_timestamp;
    unification_constraints * m_uc;
    normalizer                m_normalizer;
    cache                     m_cache;

    expr normalize(expr const & e, context const & ctx) {
        return m_normalizer(e, ctx);
    }

    expr check_type(expr const & e, expr const & s, context const & ctx) {
        if (is_type(e))
            return e;
        if (is_bool(e))
            return Type();
        expr u = normalize(e, ctx);
        if (is_type(u))
            return u;
        if (is_bool(u))
            return Type();
        if (has_metavar(u) && m_menv) {
            justification jst = mk_type_expected_justification(ctx, s);
            m_uc->push_back(mk_convertible_constraint(ctx, u, TypeU, jst));
            return u;
        }
        throw type_expected_exception(m_env, ctx, s);
    }

    expr get_range(expr t, expr const & e, context const & ctx) {
        unsigned num = num_args(e) - 1;
        while (num > 0) {
            --num;
            if (is_pi(t)) {
                t = abst_body(t);
            } else {
                t = m_normalizer(t, ctx);
                if (is_pi(t)) {
                    t = abst_body(t);
                } else if (has_metavar(t) && m_menv) {
                    // Create two fresh variables A and B,
                    // and assign r == (Pi(x : A), B x)
                    expr A   = m_menv->mk_metavar(ctx);
                    expr B   = m_menv->mk_metavar(ctx);
                    expr p   = mk_pi(g_x_name, A, B(Var(0)));
                    justification jst = mk_function_expected_justification(ctx, e);
                    m_uc->push_back(mk_eq_constraint(ctx, t, p, jst));
                    t        = abst_body(p);
                } else {
                    throw function_expected_exception(m_env, ctx, e);
                }
            }
        }
        if (closed(t))
            return t;
        else
            return instantiate(t, num_args(e)-1, &arg(e, 1));
    }

    void set_menv(metavar_env * menv) {
        if (m_menv == menv) {
            // Check whether m_menv has been updated since the last time the checker has been invoked
            if (m_menv && m_menv->get_timestamp() > m_menv_timestamp) {
                m_menv_timestamp = m_menv->get_timestamp();
                m_cache.clear();
            }
        } else {
            m_menv = menv;
            m_cache.clear();
            m_menv_timestamp = m_menv ? m_menv->get_timestamp() : 0;
        }
    }

    expr infer_type(expr const & e, context const & ctx) {
        // cheap cases, we do not cache results
        switch (e.kind()) {
        case expr_kind::MetaVar:
            if (m_menv) {
                if (m_menv->is_assigned(e))
                    return infer_type(m_menv->get_subst(e), ctx);
                else
                    return m_menv->get_type(e);
            } else {
                throw unexpected_metavar_occurrence(m_env, e);
            }
        case expr_kind::Constant: {
            if (const_type(e)) {
                return const_type(e);
            } else {
                object const & obj = m_env.get_object(const_name(e));
                if (obj.has_type())
                    return obj.get_type();
                else
                    throw has_no_type_exception(m_env, e);
            }
            break;
        }
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            if (ce.get_domain()) {
                context const & ce_ctx   = p.second;
                return lift_free_vars(ce.get_domain(), ctx.size() - ce_ctx.size());
            }
            // Remark: the case where ce.get_domain() is not
            // available is not considered cheap.
            break;
        }
        case expr_kind::Eq:
            return mk_bool_type();
        case expr_kind::Value:
            return to_value(e).get_type();
        case expr_kind::Type:
            return mk_type(ty_level(e) + 1);
        case expr_kind::App: case expr_kind::Lambda:
        case expr_kind::Pi:  case expr_kind::Let:
            break; // expensive cases
        }

        check_interrupted();
        bool shared = false;
        if (is_shared(e)) {
            shared = true;
            auto it = m_cache.find(e);
            if (it != m_cache.end())
                return it->second;
        }

        expr r;
        switch (e.kind()) {
        case expr_kind::Constant: case expr_kind::Eq:
        case expr_kind::Value:    case expr_kind::Type:
        case expr_kind::MetaVar:
            lean_unreachable(); // LCOV_EXCL_LINE
        case expr_kind::Var: {
            auto p = lookup_ext(ctx, var_idx(e));
            context_entry const & ce = p.first;
            context const & ce_ctx   = p.second;
            lean_assert(!ce.get_domain());
            r = lift_free_vars(infer_type(ce.get_body(), ce_ctx), ctx.size() - ce_ctx.size());
            break;
        }
        case expr_kind::App: {
            expr const & f = arg(e, 0);
            expr f_t = infer_type(f, ctx);
            r = get_range(f_t, e, ctx);
            break;
        }
        case expr_kind::Lambda: {
            cache::mk_scope sc(m_cache);
            r = mk_pi(abst_name(e), abst_domain(e), infer_type(abst_body(e), extend(ctx, abst_name(e), abst_domain(e))));
            break;
        }
        case expr_kind::Pi: {
            expr t1  = check_type(infer_type(abst_domain(e), ctx), abst_domain(e), ctx);
            expr t2;
            {
                cache::mk_scope sc(m_cache);
                context new_ctx = extend(ctx, abst_name(e), abst_domain(e));
                t2 = check_type(infer_type(abst_body(e), new_ctx), abst_body(e), new_ctx);
            }
            if (is_type(t1) && is_type(t2)) {
                r = mk_type(max(ty_level(t1), ty_level(t2)));
            } else {
                lean_assert(m_uc);
                justification jst = mk_max_type_justification(ctx, e);
                r = m_menv->mk_metavar(ctx);
                m_uc->push_back(mk_max_constraint(ctx, t1, t2, r, jst));
            }
            break;
        }
        case expr_kind::Let: {
            cache::mk_scope sc(m_cache);
            r = infer_type(let_body(e), extend(ctx, let_name(e), let_type(e), let_value(e)));
            break;
        }}

        if (shared) {
            m_cache.insert(e, r);
        }
        return r;
    }

    void set_ctx(context const & ctx) {
        if (!is_eqp(m_ctx, ctx)) {
            clear();
            m_ctx = ctx;
        }
    }

public:
    imp(environment const & env):
        m_env(env),
        m_normalizer(env) {
        m_menv           = nullptr;
        m_menv_timestamp = 0;
        m_uc             = nullptr;
    }

    expr operator()(expr const & e, context const & ctx, metavar_env * menv, buffer<unification_constraint> & uc) {
        set_ctx(ctx);
        set_menv(menv);
        flet<unification_constraints*> set(m_uc, &uc);
        return infer_type(e, ctx);
    }

    void clear() {
        m_cache.clear();
        m_normalizer.clear();
        m_ctx            = context();
        m_menv           = nullptr;
        m_menv_timestamp = 0;
    }

    bool is_proposition(expr const & e, context const & ctx) {
        buffer<unification_constraint> uc;
        expr t = operator()(e, ctx, nullptr, uc);
        if (is_bool(t))
            return true;
        else
            return is_bool(normalize(t, ctx));
    }
};
type_inferer::type_inferer(environment const & env):m_ptr(new imp(env)) {}
type_inferer::~type_inferer() {}
expr type_inferer::operator()(expr const & e, context const & ctx, metavar_env * menv, buffer<unification_constraint> & uc) {
    return m_ptr->operator()(e, ctx, menv, uc);
}
expr type_inferer::operator()(expr const & e, context const & ctx) {
    buffer<unification_constraint> uc;
    return operator()(e, ctx, nullptr, uc);
}
bool type_inferer::is_proposition(expr const & e, context const & ctx) {
    return m_ptr->is_proposition(e, ctx);
}
void type_inferer::clear() { m_ptr->clear(); }

constexpr char const * type_inferer_mt = "type_inferer";
type_inferer & to_type_inferer(lua_State * L, int i) { return *static_cast<type_inferer*>(luaL_checkudata(L, i, type_inferer_mt)); }
DECL_PRED(type_inferer)
DECL_GC(type_inferer)

static int type_inferer_call(lua_State * L) {
    int nargs = lua_gettop(L);
    type_inferer & inferer = to_type_inferer(L, 1);
    if (nargs == 2)
        return push_expr(L, inferer(to_nonnull_expr(L, 2)));
    else
        return push_expr(L, inferer(to_nonnull_expr(L, 2), to_context(L, 3)));
}

static int type_inferer_clear(lua_State * L) {
    to_type_inferer(L, 1).clear();
    return 0;
}

static int mk_type_inferer(lua_State * L) {
    void * mem = lua_newuserdata(L, sizeof(type_inferer));
    new (mem) type_inferer(to_environment(L, 1));
    luaL_getmetatable(L, type_inferer_mt);
    lua_setmetatable(L, -2);
    return 1;
}

static const struct luaL_Reg type_inferer_m[] = {
    {"__gc",            type_inferer_gc}, // never throws
    {"__call",          safe_function<type_inferer_call>},
    {"clear",           safe_function<type_inferer_clear>},
    {0, 0}
};

void open_type_inferer(lua_State * L) {
    luaL_newmetatable(L, type_inferer_mt);
    lua_pushvalue(L, -1);
    lua_setfield(L, -2, "__index");
    setfuncs(L, type_inferer_m, 0);

    SET_GLOBAL_FUN(mk_type_inferer,          "type_inferer");
    SET_GLOBAL_FUN(type_inferer_pred,        "is_type_inferer");
}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "ArrayPtr.h"
#include "Context.h"
#include "Deserializer.h"
#include "Log.h"
#include "Profiler.h"
#include "ResourceCache.h"
#include "Serializer.h"
#include "XMLFile.h"

#include <pugixml.hpp>

#include "DebugNew.h"

namespace Urho3D
{

/// XML writer for pugixml.
class XMLWriter : public pugi::xml_writer
{
public:
    /// Construct.
    XMLWriter(Serializer& dest) :
        dest_(dest),
        success_(true)
    {
    }

    /// Write bytes to output.
    void write(const void* data, size_t size)
    {
        if (dest_.Write(data, size) != size)
            success_ = false;
    }

    /// Destination serializer.
    Serializer& dest_;
    /// Success flag.
    bool success_;
};

XMLFile::XMLFile(Context* context) :
    Resource(context),
    document_(new pugi::xml_document())
{
}

XMLFile::~XMLFile()
{
    delete document_;
    document_ = 0;
}

void XMLFile::RegisterObject(Context* context)
{
    context->RegisterFactory<XMLFile>();
}

bool XMLFile::Load(Deserializer& source)
{
    PROFILE(LoadXMLFile);

    unsigned dataSize = source.GetSize();
    if (!dataSize && !source.GetName().Empty())
    {
        LOGERROR("Zero sized XML data in " + source.GetName());
        return false;
    }

    SharedArrayPtr<char> buffer(new char[dataSize]);
    if (source.Read(buffer.Get(), dataSize) != dataSize)
        return false;

    if (!document_->load_buffer(buffer.Get(), dataSize))
    {
        LOGERROR("Could not parse XML data from " + source.GetName());
        return false;
    }

    XMLElement rootElem = GetRoot();
    String inherit = rootElem ? rootElem.GetAttribute("inherit") : String::EMPTY;
    if (!inherit.Empty())
    {
        // The existence of this attribute indicates this is an RFC 5261 patch file
        ResourceCache* cache = GetSubsystem<ResourceCache>();
        XMLFile* inheritedXMLFile = cache->GetResource<XMLFile>(inherit);
        if (!inheritedXMLFile)
        {
            LOGERRORF("Could not find inherit RenderPath XML file: %s", inherit.CString());
            return false;
        }

        // Patch this XMLFile and leave the original inherited XMLFile as it is
        pugi::xml_document* patchDocument = document_;
        document_ = new pugi::xml_document();
        document_->reset(*inheritedXMLFile->document_);      // Unfortunately there is no way to adjust the data size correctly
        Patch(rootElem);
        delete patchDocument;
    }

    // Note: this probably does not reflect internal data structure size accurately
    SetMemoryUse(dataSize);
    return true;
}

bool XMLFile::Save(Serializer& dest) const
{
    XMLWriter writer(dest);
    document_->save(writer);
    return writer.success_;
}

XMLElement XMLFile::CreateRoot(const String& name)
{
    document_->reset();
    pugi::xml_node root = document_->append_child(name.CString());
    return XMLElement(this, root.internal_object());
}

XMLElement XMLFile::GetRoot(const String& name)
{
    pugi::xml_node root = document_->first_child();
    if (root.empty())
        return XMLElement();

    if (!name.Empty() && name != root.name())
        return XMLElement();
    else
        return XMLElement(this, root.internal_object());
}

void XMLFile::Patch(XMLFile* patchFile)
{
    Patch(patchFile->GetRoot());
}

void XMLFile::Patch(XMLElement patchElement)
{
    pugi::xml_node root = pugi::xml_node(patchElement.GetNode());

    for (pugi::xml_node::iterator patch = root.begin(); patch != root.end(); patch++)
    {
        pugi::xml_attribute sel = patch->attribute("sel");
        if (sel.empty())
        {
            LOGERROR("XML Patch failed due to node not having a sel attribute.");
            continue;
        }

        // Only select a single node at a time, they can use xpath to select specific ones in multiple otherwise the node set becomes invalid due to changes
        pugi::xpath_node original = document_->select_single_node(sel.value());
        if (!original)
        {
            LOGERRORF("XML Patch failed with bad select: %s.", sel.value());
            continue;
        }

        if (strcmp(patch->name(),"add") == 0)
            PatchAdd(*patch, original);
        else if (strcmp(patch->name(), "replace") == 0)
            PatchReplace(*patch, original);
        else if (strcmp(patch->name(), "remove") == 0)
            PatchRemove(original);
        else
            LOGERROR("XMLFiles used for patching should only use 'add', 'replace' or 'remove' elements.");
    }
}

void XMLFile::PatchAdd(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    // If not a node, log an error
    if (original.attribute())
    {
        LOGERRORF("XML Patch failed calling Add due to not selecting a node, %s attribute was selected.", original.attribute().name());
        return;
    }

    // If no type add node, if contains '@' treat as attribute
    pugi::xml_attribute type = patch.attribute("type");
    if (!type || strlen(type.value()) <= 0)
        AddNode(patch, original);
    else if (type.value()[0] == '@')
        AddAttribute(patch, original);
}

void XMLFile::PatchReplace(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    // If no attribute but node then its a node, otherwise its an attribute or null
    if (!original.attribute() && original.node())
    {
        pugi::xml_node parent = original.node().parent();

        parent.insert_copy_before(patch.first_child(), original.node());
        parent.remove_child(original.node());
    }
    else if (original.attribute())
    {
        original.attribute().set_value(patch.child_value());
    }
}

void XMLFile::PatchRemove(const pugi::xpath_node& original)
{
    // If no attribute but node then its a node, otherwise its an attribute or null
    if (!original.attribute() && original.node())
    {
        pugi::xml_node parent = original.parent();
        parent.remove_child(original.node());
    }
    else if (original.attribute())
    {
        pugi::xml_node parent = original.parent();
        parent.remove_attribute(original.attribute());
    }
}

void XMLFile::AddNode(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    // If pos is null, append or prepend add as a child, otherwise add before or after, the default is to append as a child
    pugi::xml_attribute pos = patch.attribute("pos");
    if (!pos || strlen(pos.value()) <= 0 || pos.value() == "append")
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the first node of the nodes to add
        if (CombineText(patch.first_child(), original.node().last_child(), false))
            start++;

        for (; start != end; start++)
            original.node().append_copy(*start);
    }
    else if (strcmp(pos.value(), "prepend") == 0)
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the last node of the nodes to add
        if (CombineText(patch.last_child(), original.node().first_child(), true))
            end--;

        pugi::xml_node pos = original.node().first_child();
        for (; start != end; start++)
            original.node().insert_copy_before(*start, pos);
    }
    else if (strcmp(pos.value(), "before") == 0)
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the first node of the nodes to add
        if (CombineText(patch.first_child(), original.node().previous_sibling(), false))
            start++;

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the last node of the nodes to add
        if (CombineText(patch.last_child(), original.node(), true))
            end--;

        for (; start != end; start++)
            original.parent().insert_copy_before(*start, original.node());
    }
    else if (strcmp(pos.value(), "after") == 0)
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the first node of the nodes to add
        if (CombineText(patch.first_child(), original.node(), false))
            start++;

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the last node of the nodes to add
        if (CombineText(patch.last_child(), original.node().next_sibling(), true))
            end--;

        pugi::xml_node pos = original.node();
        for (; start != end; start++)
            pos = original.parent().insert_copy_after(*start, pos);
    }
}

void XMLFile::AddAttribute(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    pugi::xml_attribute attribute = patch.attribute("type");

    if (!patch.first_child() && patch.first_child().type() != pugi::node_pcdata)
    {
        LOGERRORF("XML Patch failed calling Add due to attempting to add non text to an attribute for %s.", attribute.value());
        return;
    }

    String name(attribute.value());
    name = name.Substring(1);

    pugi::xml_attribute newAttribute = original.node().append_attribute(name.CString());
    newAttribute.set_value(patch.child_value());
}

bool XMLFile::CombineText(const pugi::xml_node& patch, pugi::xml_node original, bool prepend)
{
    if (!patch || !original)
        return false;

    if ((patch.type() == pugi::node_pcdata && original.type() == pugi::node_pcdata) ||
        (patch.type() == pugi::node_cdata && original.type() == pugi::node_cdata))
    {
        if (prepend)
            original.set_value(ToString("%s%s", patch.value(), original.value()).CString());
        else
            original.set_value(ToString("%s%s", original.value(), patch.value()).CString());

        return true;
    }

    return false;
}

}
<commit_msg>Code cleanup. [ci skip]<commit_after>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "ArrayPtr.h"
#include "Context.h"
#include "Deserializer.h"
#include "Log.h"
#include "Profiler.h"
#include "ResourceCache.h"
#include "Serializer.h"
#include "XMLFile.h"

#include <pugixml.hpp>

#include "DebugNew.h"

namespace Urho3D
{

/// XML writer for pugixml.
class XMLWriter : public pugi::xml_writer
{
public:
    /// Construct.
    XMLWriter(Serializer& dest) :
        dest_(dest),
        success_(true)
    {
    }

    /// Write bytes to output.
    void write(const void* data, size_t size)
    {
        if (dest_.Write(data, size) != size)
            success_ = false;
    }

    /// Destination serializer.
    Serializer& dest_;
    /// Success flag.
    bool success_;
};

XMLFile::XMLFile(Context* context) :
    Resource(context),
    document_(new pugi::xml_document())
{
}

XMLFile::~XMLFile()
{
    delete document_;
    document_ = 0;
}

void XMLFile::RegisterObject(Context* context)
{
    context->RegisterFactory<XMLFile>();
}

bool XMLFile::Load(Deserializer& source)
{
    PROFILE(LoadXMLFile);

    unsigned dataSize = source.GetSize();
    if (!dataSize && !source.GetName().Empty())
    {
        LOGERROR("Zero sized XML data in " + source.GetName());
        return false;
    }

    SharedArrayPtr<char> buffer(new char[dataSize]);
    if (source.Read(buffer.Get(), dataSize) != dataSize)
        return false;

    if (!document_->load_buffer(buffer.Get(), dataSize))
    {
        LOGERROR("Could not parse XML data from " + source.GetName());
        return false;
    }

    XMLElement rootElem = GetRoot();
    String inherit = rootElem.GetAttribute("inherit");
    if (!inherit.Empty())
    {
        // The existence of this attribute indicates this is an RFC 5261 patch file
        ResourceCache* cache = GetSubsystem<ResourceCache>();
        XMLFile* inheritedXMLFile = cache->GetResource<XMLFile>(inherit);
        if (!inheritedXMLFile)
        {
            LOGERRORF("Could not find inherit RenderPath XML file: %s", inherit.CString());
            return false;
        }

        // Patch this XMLFile and leave the original inherited XMLFile as it is
        pugi::xml_document* patchDocument = document_;
        document_ = new pugi::xml_document();
        document_->reset(*inheritedXMLFile->document_);      // Unfortunately there is no way to adjust the data size correctly
        Patch(rootElem);
        delete patchDocument;
    }

    // Note: this probably does not reflect internal data structure size accurately
    SetMemoryUse(dataSize);
    return true;
}

bool XMLFile::Save(Serializer& dest) const
{
    XMLWriter writer(dest);
    document_->save(writer);
    return writer.success_;
}

XMLElement XMLFile::CreateRoot(const String& name)
{
    document_->reset();
    pugi::xml_node root = document_->append_child(name.CString());
    return XMLElement(this, root.internal_object());
}

XMLElement XMLFile::GetRoot(const String& name)
{
    pugi::xml_node root = document_->first_child();
    if (root.empty())
        return XMLElement();

    if (!name.Empty() && name != root.name())
        return XMLElement();
    else
        return XMLElement(this, root.internal_object());
}

void XMLFile::Patch(XMLFile* patchFile)
{
    Patch(patchFile->GetRoot());
}

void XMLFile::Patch(XMLElement patchElement)
{
    pugi::xml_node root = pugi::xml_node(patchElement.GetNode());

    for (pugi::xml_node::iterator patch = root.begin(); patch != root.end(); patch++)
    {
        pugi::xml_attribute sel = patch->attribute("sel");
        if (sel.empty())
        {
            LOGERROR("XML Patch failed due to node not having a sel attribute.");
            continue;
        }

        // Only select a single node at a time, they can use xpath to select specific ones in multiple otherwise the node set becomes invalid due to changes
        pugi::xpath_node original = document_->select_single_node(sel.value());
        if (!original)
        {
            LOGERRORF("XML Patch failed with bad select: %s.", sel.value());
            continue;
        }

        if (strcmp(patch->name(),"add") == 0)
            PatchAdd(*patch, original);
        else if (strcmp(patch->name(), "replace") == 0)
            PatchReplace(*patch, original);
        else if (strcmp(patch->name(), "remove") == 0)
            PatchRemove(original);
        else
            LOGERROR("XMLFiles used for patching should only use 'add', 'replace' or 'remove' elements.");
    }
}

void XMLFile::PatchAdd(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    // If not a node, log an error
    if (original.attribute())
    {
        LOGERRORF("XML Patch failed calling Add due to not selecting a node, %s attribute was selected.", original.attribute().name());
        return;
    }

    // If no type add node, if contains '@' treat as attribute
    pugi::xml_attribute type = patch.attribute("type");
    if (!type || strlen(type.value()) <= 0)
        AddNode(patch, original);
    else if (type.value()[0] == '@')
        AddAttribute(patch, original);
}

void XMLFile::PatchReplace(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    // If no attribute but node then its a node, otherwise its an attribute or null
    if (!original.attribute() && original.node())
    {
        pugi::xml_node parent = original.node().parent();

        parent.insert_copy_before(patch.first_child(), original.node());
        parent.remove_child(original.node());
    }
    else if (original.attribute())
    {
        original.attribute().set_value(patch.child_value());
    }
}

void XMLFile::PatchRemove(const pugi::xpath_node& original)
{
    // If no attribute but node then its a node, otherwise its an attribute or null
    if (!original.attribute() && original.node())
    {
        pugi::xml_node parent = original.parent();
        parent.remove_child(original.node());
    }
    else if (original.attribute())
    {
        pugi::xml_node parent = original.parent();
        parent.remove_attribute(original.attribute());
    }
}

void XMLFile::AddNode(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    // If pos is null, append or prepend add as a child, otherwise add before or after, the default is to append as a child
    pugi::xml_attribute pos = patch.attribute("pos");
    if (!pos || strlen(pos.value()) <= 0 || pos.value() == "append")
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the first node of the nodes to add
        if (CombineText(patch.first_child(), original.node().last_child(), false))
            start++;

        for (; start != end; start++)
            original.node().append_copy(*start);
    }
    else if (strcmp(pos.value(), "prepend") == 0)
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the last node of the nodes to add
        if (CombineText(patch.last_child(), original.node().first_child(), true))
            end--;

        pugi::xml_node pos = original.node().first_child();
        for (; start != end; start++)
            original.node().insert_copy_before(*start, pos);
    }
    else if (strcmp(pos.value(), "before") == 0)
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the first node of the nodes to add
        if (CombineText(patch.first_child(), original.node().previous_sibling(), false))
            start++;

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the last node of the nodes to add
        if (CombineText(patch.last_child(), original.node(), true))
            end--;

        for (; start != end; start++)
            original.parent().insert_copy_before(*start, original.node());
    }
    else if (strcmp(pos.value(), "after") == 0)
    {
        pugi::xml_node::iterator start = patch.begin();
        pugi::xml_node::iterator end = patch.end();

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the first node of the nodes to add
        if (CombineText(patch.first_child(), original.node(), false))
            start++;

        // There can not be two consecutive text nodes, so check to see if they need to be combined
        // If they have been we can skip the last node of the nodes to add
        if (CombineText(patch.last_child(), original.node().next_sibling(), true))
            end--;

        pugi::xml_node pos = original.node();
        for (; start != end; start++)
            pos = original.parent().insert_copy_after(*start, pos);
    }
}

void XMLFile::AddAttribute(const pugi::xml_node& patch, pugi::xpath_node& original)
{
    pugi::xml_attribute attribute = patch.attribute("type");

    if (!patch.first_child() && patch.first_child().type() != pugi::node_pcdata)
    {
        LOGERRORF("XML Patch failed calling Add due to attempting to add non text to an attribute for %s.", attribute.value());
        return;
    }

    String name(attribute.value());
    name = name.Substring(1);

    pugi::xml_attribute newAttribute = original.node().append_attribute(name.CString());
    newAttribute.set_value(patch.child_value());
}

bool XMLFile::CombineText(const pugi::xml_node& patch, pugi::xml_node original, bool prepend)
{
    if (!patch || !original)
        return false;

    if ((patch.type() == pugi::node_pcdata && original.type() == pugi::node_pcdata) ||
        (patch.type() == pugi::node_cdata && original.type() == pugi::node_cdata))
    {
        if (prepend)
            original.set_value(ToString("%s%s", patch.value(), original.value()).CString());
        else
            original.set_value(ToString("%s%s", original.value(), patch.value()).CString());

        return true;
    }

    return false;
}

}
<|endoftext|>


<commit_before>#ifndef VPP_WINDOW_HPP__
# define VPP_WINDOW_HPP__

# include <vpp/imageNd.hh>

namespace vpp
{

  template <typename I>
  class window_iterator
  {
  public:
    typedef typename I::value_type V;
    inline window_iterator(V* p, const std::vector<int>& offsets, int i)
      : offsets_(offsets),
        p_(p),
        cur_((V*)(((char*)p) + offsets_[i])),
        i_(i)
    {
    }

    inline window_iterator<I>& operator++()
    {
      i_++;
      cur_ = (V*)((char*)(p_) + offsets_[i_]);
    }

    inline V& operator*() { return *cur_; }
    inline const V& operator*() const { return *cur_; }

    inline int i() const { return i_; }

  private:
    const std::vector<int>& offsets_;
    V* p_;
    V* cur_;
    int i_;
  };

  template <typename I>
  bool operator==(const window_iterator<I>& a, const window_iterator<I>& b)
  {
    return a.i() == b.i();
  }

  template <typename I>
  bool operator!=(const window_iterator<I>& a, const window_iterator<I>& b)
  {
    return !(a == b);
  }

  template <typename I>
  class window_range
  {
  public:
    typedef typename I::coord_type coord_type;
    typedef typename I::value_type value_type;
    typedef typename I::iterator I_iterator;

    typedef window_iterator<I> iterator;

    inline window_range(value_type* p, const std::vector<int>& offsets)
      : p_(p),
        offsets_(offsets)
    {
    }

    inline iterator begin()
    {
      return window_iterator<I>(p_, offsets_, 0);
    }

    inline iterator end()
    {
      return window_iterator<I>(p_, offsets_, offsets_.size() - 1);
    }

  private:
    value_type* p_;
    const std::vector<int>& offsets_;
  };

  template <typename I>
  class window
  {
  public:
    typedef typename I::coord_type coord_type;
    typedef typename I::value_type value_type;
    typedef typename I::iterator I_iterator;
    window(const I& img, const std::vector<coord_type>& cds)
    {
      for (auto p : cds)
        offsets_.push_back(img.offset_of(p));
      offsets_.push_back(0);
    }

    inline window_range<I> operator()(value_type& p) const
    {
      return window_range<I>(&p, offsets_);
    }

  private:
    std::vector<int> offsets_;
  };

  template <typename I>
  window<I> make_window(const I& img, const std::vector<typename I::coord_type>& cds)
  {
    return window<I>(img, cds);
  }

};

#endif

<commit_msg>New syntax for window iterations.<commit_after>#ifndef VPP_WINDOW_HPP__
# define VPP_WINDOW_HPP__

# include <vpp/imageNd.hh>

namespace vpp
{

  template <typename V>
  struct window_runner
  {
    inline window_runner(const std::vector<int>& offsets, V& pix)
      : offsets_(offsets),
        pix_((char*)&pix)
    {
    }

    template <typename F>
    inline void operator<(F f)
    {
      for (auto off : offsets_)
        f(*(V*)(pix_ + off));
    }

    const std::vector<int>& offsets_;
    char* pix_;
  };

  template <typename I>
  class window
  {
  public:
    typedef typename I::coord_type coord_type;
    typedef typename I::value_type value_type;
    typedef typename I::iterator I_iterator;
    window(const I& img, const std::vector<coord_type>& cds)
    {
      for (auto p : cds)
        offsets_.push_back(img.offset_of(p));
    }

    inline window_runner<value_type> operator()(value_type& p) const
    {
      return window_runner<value_type>(offsets_, p);
    }

  private:
    std::vector<int> offsets_;
  };

  template <typename I>
  window<I> make_window(const I& img, const std::vector<typename I::coord_type>& cds)
  {
    return window<I>(img, cds);
  }

};

#endif
<|endoftext|>