<commit_before>#include <ruby.h>
#include <ruby/encoding.h>
#include <ruby/version.h>
#include "houdini.h"
#include <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#if (RUBY_API_VERSION_MAJOR > 2) || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 1)
/* define nothing */
#else
# define RARRAY_AREF(a, i) RARRAY_PTR(a)[i]
#endif

#define FOREACH_FUNC(func) reinterpret_cast<int (*)(ANYARGS)>(func)

VALUE rb_mAttributeBuilder;
static ID id_flatten;
static ID id_hyphen;

static inline std::string
string_from_value(VALUE v)
{
  return std::string(RSTRING_PTR(v), RSTRING_LEN(v));
}

enum attribute_value_type
{
  ATTRIBUTE_TYPE_TRUE,
  ATTRIBUTE_TYPE_FALSE,
  ATTRIBUTE_TYPE_VALUE,
};

struct attribute_value
{
  attribute_value_type type_;
  std::string str_;

  attribute_value(attribute_value_type type) : type_(type) {}
  attribute_value(const char *cstr, long len) : type_(ATTRIBUTE_TYPE_VALUE), str_(cstr, len) {}
  attribute_value(const std::string& str) : type_(ATTRIBUTE_TYPE_VALUE), str_(str) {}

  static attribute_value from_value(VALUE value)
  {
    if (RB_TYPE_P(value, T_TRUE)) {
      return attribute_value(ATTRIBUTE_TYPE_TRUE);
    } else if (!RTEST(value)) {
      return attribute_value(ATTRIBUTE_TYPE_FALSE);
    } else {
      value = rb_convert_type(value, T_STRING, "String", "to_s");
      return attribute_value(string_from_value(value));
    }
  }
};

typedef std::map<std::string, attribute_value> attributes_type;

void upsert_attribute(attributes_type& m, const std::string& key, const attribute_value& value)
{
  const std::pair<attributes_type::iterator, bool> r = m.insert(std::make_pair(key, value));
  if (!r.second) {
    r.first->second = value;
  }
}

struct attribute_holder
{
  std::vector<attribute_value> ids_, classes_;
  attributes_type m_;

  inline void upsert(const std::string& key, const attribute_value& value)
  {
    return upsert_attribute(m_, key, value);
  }
};

static VALUE
to_value(const attributes_type& m)
{
  VALUE h = rb_hash_new();
  for (attributes_type::const_iterator it = m.begin(); it != m.end(); ++it) {
    VALUE k = rb_enc_str_new(it->first.data(), it->first.size(), rb_utf8_encoding());
    VALUE v = Qnil;
    switch (it->second.type_) {
      case ATTRIBUTE_TYPE_TRUE:
        v = Qtrue;
        break;
      case ATTRIBUTE_TYPE_FALSE:
        v = Qnil;
        break;
      case ATTRIBUTE_TYPE_VALUE:
        v = rb_enc_str_new(it->second.str_.data(), it->second.str_.size(), rb_utf8_encoding());
        break;
    }
    rb_hash_aset(h, k, v);
  }
  return h;
}

static void
concat_array_attribute(std::vector<attribute_value>& ary, VALUE value)
{
  if (RB_TYPE_P(value, T_ARRAY)) {
    value = rb_funcall(value, id_flatten, 0);
  } else {
    value = rb_Array(value);
  }
  const long len = RARRAY_LEN(value);
  for (long i = 0; i < len; i++) {
    ary.push_back(attribute_value::from_value(RARRAY_AREF(value, i)));
  }
}

static attributes_type normalize_data(VALUE data);

static int
normalize_data_i(VALUE key, VALUE value, VALUE arg)
{
  attributes_type *normalized = reinterpret_cast<attributes_type *>(arg);
  key = rb_convert_type(key, T_STRING, "String", "to_s");
  std::string key_ = string_from_value(key);
  std::replace(key_.begin(), key_.end(), '_', '-');

  if (RB_TYPE_P(value, T_HASH)) {
    const attributes_type sub = normalize_data(value);
    for (attributes_type::const_iterator it = sub.begin(); it != sub.end(); ++it) {
      upsert_attribute(*normalized, key_ + "-" + it->first, it->second);
    }
  } else {
    upsert_attribute(*normalized, key_, attribute_value::from_value(value));
  }
  return ST_CONTINUE;
}

static attributes_type
normalize_data(VALUE data)
{
  Check_Type(data, T_HASH);
  attributes_type m;
  rb_hash_foreach(data, FOREACH_FUNC(normalize_data_i), reinterpret_cast<VALUE>(&m));
  return m;
}

static int
merge_one_i(VALUE key, VALUE value, VALUE arg)
{
  attribute_holder *attributes = reinterpret_cast<attribute_holder *>(arg);

  key = rb_convert_type(key, T_STRING, "String", "to_s");
  const std::string key_ = string_from_value(key);
  if (key_ == "class") {
    concat_array_attribute(attributes->classes_, value);
  } else if (key_ == "id") {
    concat_array_attribute(attributes->ids_, value);
  } else if (key_ == "data" && RB_TYPE_P(value, T_HASH)) {
    const attributes_type data = normalize_data(value);
    for (attributes_type::const_iterator it = data.begin(); it != data.end(); ++it) {
      attributes->upsert("data-" + it->first, it->second);
    }
  } else {
    attributes->upsert(key_, attribute_value::from_value(value));
  }
  return ST_CONTINUE;
}

static void
merge_one(attribute_holder& attributes, VALUE h)
{
  Check_Type(h, T_HASH);
  rb_hash_foreach(h, FOREACH_FUNC(merge_one_i), reinterpret_cast<VALUE>(&attributes));
}

static void
join_class_attribute(attribute_holder& attributes)
{
  const std::vector<attribute_value>& classes = attributes.classes_;
  std::vector<std::string> ary;

  for (std::vector<attribute_value>::const_iterator it = classes.begin(); it != classes.end(); ++it) {
    switch (it->type_) {
      case ATTRIBUTE_TYPE_FALSE:
        break;
      case ATTRIBUTE_TYPE_TRUE:
        ary.push_back("true");
        break;
      case ATTRIBUTE_TYPE_VALUE:
        size_t prev = 0, pos;
        while ((pos = it->str_.find_first_of(' ', prev)) != std::string::npos) {
          if (pos != prev) {
            ary.push_back(std::string(it->str_, prev, pos - prev));
          }
          prev = pos+1;
        }
        ary.push_back(std::string(it->str_, prev, it->str_.size() - prev));
        break;
    }
  }
  if (ary.empty()) {
    return;
  }

  std::sort(ary.begin(), ary.end());
  ary.erase(std::unique(ary.begin(), ary.end()), ary.end());
  std::ostringstream oss;
  for (std::vector<std::string>::const_iterator it = ary.begin(); it != ary.end(); ++it) {
    if (it != ary.begin()) {
      oss << ' ';
    }
    oss << *it;
  }
  attributes.upsert("class", attribute_value(oss.str()));
}

static void
join_id_attribute(attribute_holder& attributes)
{
  const std::vector<attribute_value>& ids = attributes.ids_;
  std::ostringstream oss;

  for (std::vector<attribute_value>::const_iterator it = ids.begin(); it != ids.end(); ++it) {
    switch (it->type_) {
      case ATTRIBUTE_TYPE_FALSE:
        break;
      case ATTRIBUTE_TYPE_TRUE:
        if (!oss.str().empty()) {
          oss << '_';
        }
        oss << "true";
        break;
      case ATTRIBUTE_TYPE_VALUE:
        if (!oss.str().empty()) {
          oss << '_';
        }
        oss << it->str_;
        break;
    }
  }
  if (oss.str().empty()) {
    return;
  }

  attributes.upsert("id", attribute_value(oss.str()));
}

static void
delete_falsey_values(attributes_type& m)
{
  for (attributes_type::iterator it = m.begin(); it != m.end();) {
    if (it->second.type_ == ATTRIBUTE_TYPE_FALSE) {
      attributes_type::iterator jt = it;
      ++it;
      m.erase(jt);
    } else {
      ++it;
    }
  }
}

static attributes_type
merge(VALUE object_ref, int argc, VALUE *argv)
{
  int i;
  attribute_holder attributes;

  for (i = 0; i < argc; i++) {
    merge_one(attributes, argv[i]);
  }
  if (!NIL_P(object_ref)) {
    merge_one(attributes, object_ref);
  }

  join_class_attribute(attributes);
  join_id_attribute(attributes);
  delete_falsey_values(attributes.m_);

  return attributes.m_;
}

static void
put_attribute(std::ostringstream& oss, const std::string& attr_quote, const std::string& key, const std::string& value)
{
  oss << " " << key << "=" << attr_quote;

  gh_buf ob = GH_BUF_INIT;
  if (houdini_escape_html0(&ob, (const uint8_t *)value.data(), value.size(), 0)) {
    oss << std::string(ob.ptr, ob.size);
    gh_buf_free(&ob);
  } else {
    oss << value;
  }
  oss << attr_quote;
}

static void
build_attribute(std::ostringstream& oss, const std::string& attr_quote, int is_html, const std::string& key, const attribute_value& value)
{
  if (value.type_ == ATTRIBUTE_TYPE_TRUE) {
    if (is_html) {
      oss << ' ' << key;
    } else {
      put_attribute(oss, attr_quote, key, key);
    }
  } else {
    put_attribute(oss, attr_quote, key, value.str_);
  }
}

static VALUE
m_build(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
{
  VALUE object_ref;
  int is_html;

  rb_check_arity(argc, 3, UNLIMITED_ARGUMENTS);
  Check_Type(argv[0], T_STRING);
  const std::string attr_quote = string_from_value(argv[0]);
  is_html = RTEST(argv[1]);
  object_ref = argv[2];
  const attributes_type attributes = merge(object_ref, argc-3, argv+3);

  std::ostringstream oss;
  for (attributes_type::const_iterator it = attributes.begin(); it != attributes.end(); ++it) {
    build_attribute(oss, attr_quote, is_html, it->first, it->second);
  }

  return rb_enc_str_new(oss.str().data(), oss.str().size(), rb_utf8_encoding());
}

static VALUE
m_normalize_data(RB_UNUSED_VAR(VALUE self), VALUE data)
{
  return to_value(normalize_data(data));
}

static VALUE
m_merge(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
{
  rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
  return to_value(merge(argv[0], argc-1, argv+1));
}

extern "C" {
void
Init_attribute_builder(void)
{
  VALUE mFaml = rb_define_module("Faml");
  rb_mAttributeBuilder = rb_define_module_under(mFaml, "AttributeBuilder");
  rb_define_singleton_method(rb_mAttributeBuilder, "build", RUBY_METHOD_FUNC(m_build), -1);
  rb_define_singleton_method(rb_mAttributeBuilder, "normalize_data", RUBY_METHOD_FUNC(m_normalize_data), 1);
  rb_define_singleton_method(rb_mAttributeBuilder, "merge", RUBY_METHOD_FUNC(m_merge), -1);

  id_flatten = rb_intern("flatten");
  id_hyphen = rb_intern("HYPHEN");
  rb_const_set(rb_mAttributeBuilder, id_hyphen, rb_obj_freeze(rb_str_new_cstr("-")));
}
};
<commit_msg>Use rb_utf8_str_new if available<commit_after>#include <ruby.h>
#include <ruby/version.h>
#include "houdini.h"
#include <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

/* faml requires Ruby >= 2.0.0 */
/* RARRAY_AREF() is available since Ruby 2.1 */
#if RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR < 1
# define RARRAY_AREF(a, i) RARRAY_PTR(a)[i]
#endif
/* rb_utf8_str_new() is available since Ruby 2.2 */
#if RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR < 2
# include <ruby/encoding.h>
# define rb_utf8_str_new(ptr, len) rb_enc_str_new(ptr, len, rb_utf8_encoding())
#endif

#define FOREACH_FUNC(func) reinterpret_cast<int (*)(ANYARGS)>(func)

VALUE rb_mAttributeBuilder;
static ID id_flatten;
static ID id_hyphen;

static inline std::string
string_from_value(VALUE v)
{
  return std::string(RSTRING_PTR(v), RSTRING_LEN(v));
}

enum attribute_value_type
{
  ATTRIBUTE_TYPE_TRUE,
  ATTRIBUTE_TYPE_FALSE,
  ATTRIBUTE_TYPE_VALUE,
};

struct attribute_value
{
  attribute_value_type type_;
  std::string str_;

  attribute_value(attribute_value_type type) : type_(type) {}
  attribute_value(const char *cstr, long len) : type_(ATTRIBUTE_TYPE_VALUE), str_(cstr, len) {}
  attribute_value(const std::string& str) : type_(ATTRIBUTE_TYPE_VALUE), str_(str) {}

  static attribute_value from_value(VALUE value)
  {
    if (RB_TYPE_P(value, T_TRUE)) {
      return attribute_value(ATTRIBUTE_TYPE_TRUE);
    } else if (!RTEST(value)) {
      return attribute_value(ATTRIBUTE_TYPE_FALSE);
    } else {
      value = rb_convert_type(value, T_STRING, "String", "to_s");
      return attribute_value(string_from_value(value));
    }
  }
};

typedef std::map<std::string, attribute_value> attributes_type;

void upsert_attribute(attributes_type& m, const std::string& key, const attribute_value& value)
{
  const std::pair<attributes_type::iterator, bool> r = m.insert(std::make_pair(key, value));
  if (!r.second) {
    r.first->second = value;
  }
}

struct attribute_holder
{
  std::vector<attribute_value> ids_, classes_;
  attributes_type m_;

  inline void upsert(const std::string& key, const attribute_value& value)
  {
    return upsert_attribute(m_, key, value);
  }
};

static VALUE
to_value(const attributes_type& m)
{
  VALUE h = rb_hash_new();
  for (attributes_type::const_iterator it = m.begin(); it != m.end(); ++it) {
    VALUE k = rb_utf8_str_new(it->first.data(), it->first.size());
    VALUE v = Qnil;
    switch (it->second.type_) {
      case ATTRIBUTE_TYPE_TRUE:
        v = Qtrue;
        break;
      case ATTRIBUTE_TYPE_FALSE:
        v = Qnil;
        break;
      case ATTRIBUTE_TYPE_VALUE:
        v = rb_utf8_str_new(it->second.str_.data(), it->second.str_.size());
        break;
    }
    rb_hash_aset(h, k, v);
  }
  return h;
}

static void
concat_array_attribute(std::vector<attribute_value>& ary, VALUE value)
{
  if (RB_TYPE_P(value, T_ARRAY)) {
    value = rb_funcall(value, id_flatten, 0);
  } else {
    value = rb_Array(value);
  }
  const long len = RARRAY_LEN(value);
  for (long i = 0; i < len; i++) {
    ary.push_back(attribute_value::from_value(RARRAY_AREF(value, i)));
  }
}

static attributes_type normalize_data(VALUE data);

static int
normalize_data_i(VALUE key, VALUE value, VALUE arg)
{
  attributes_type *normalized = reinterpret_cast<attributes_type *>(arg);
  key = rb_convert_type(key, T_STRING, "String", "to_s");
  std::string key_ = string_from_value(key);
  std::replace(key_.begin(), key_.end(), '_', '-');

  if (RB_TYPE_P(value, T_HASH)) {
    const attributes_type sub = normalize_data(value);
    for (attributes_type::const_iterator it = sub.begin(); it != sub.end(); ++it) {
      upsert_attribute(*normalized, key_ + "-" + it->first, it->second);
    }
  } else {
    upsert_attribute(*normalized, key_, attribute_value::from_value(value));
  }
  return ST_CONTINUE;
}

static attributes_type
normalize_data(VALUE data)
{
  Check_Type(data, T_HASH);
  attributes_type m;
  rb_hash_foreach(data, FOREACH_FUNC(normalize_data_i), reinterpret_cast<VALUE>(&m));
  return m;
}

static int
merge_one_i(VALUE key, VALUE value, VALUE arg)
{
  attribute_holder *attributes = reinterpret_cast<attribute_holder *>(arg);

  key = rb_convert_type(key, T_STRING, "String", "to_s");
  const std::string key_ = string_from_value(key);
  if (key_ == "class") {
    concat_array_attribute(attributes->classes_, value);
  } else if (key_ == "id") {
    concat_array_attribute(attributes->ids_, value);
  } else if (key_ == "data" && RB_TYPE_P(value, T_HASH)) {
    const attributes_type data = normalize_data(value);
    for (attributes_type::const_iterator it = data.begin(); it != data.end(); ++it) {
      attributes->upsert("data-" + it->first, it->second);
    }
  } else {
    attributes->upsert(key_, attribute_value::from_value(value));
  }
  return ST_CONTINUE;
}

static void
merge_one(attribute_holder& attributes, VALUE h)
{
  Check_Type(h, T_HASH);
  rb_hash_foreach(h, FOREACH_FUNC(merge_one_i), reinterpret_cast<VALUE>(&attributes));
}

static void
join_class_attribute(attribute_holder& attributes)
{
  const std::vector<attribute_value>& classes = attributes.classes_;
  std::vector<std::string> ary;

  for (std::vector<attribute_value>::const_iterator it = classes.begin(); it != classes.end(); ++it) {
    switch (it->type_) {
      case ATTRIBUTE_TYPE_FALSE:
        break;
      case ATTRIBUTE_TYPE_TRUE:
        ary.push_back("true");
        break;
      case ATTRIBUTE_TYPE_VALUE:
        size_t prev = 0, pos;
        while ((pos = it->str_.find_first_of(' ', prev)) != std::string::npos) {
          if (pos != prev) {
            ary.push_back(std::string(it->str_, prev, pos - prev));
          }
          prev = pos+1;
        }
        ary.push_back(std::string(it->str_, prev, it->str_.size() - prev));
        break;
    }
  }
  if (ary.empty()) {
    return;
  }

  std::sort(ary.begin(), ary.end());
  ary.erase(std::unique(ary.begin(), ary.end()), ary.end());
  std::ostringstream oss;
  for (std::vector<std::string>::const_iterator it = ary.begin(); it != ary.end(); ++it) {
    if (it != ary.begin()) {
      oss << ' ';
    }
    oss << *it;
  }
  attributes.upsert("class", attribute_value(oss.str()));
}

static void
join_id_attribute(attribute_holder& attributes)
{
  const std::vector<attribute_value>& ids = attributes.ids_;
  std::ostringstream oss;

  for (std::vector<attribute_value>::const_iterator it = ids.begin(); it != ids.end(); ++it) {
    switch (it->type_) {
      case ATTRIBUTE_TYPE_FALSE:
        break;
      case ATTRIBUTE_TYPE_TRUE:
        if (!oss.str().empty()) {
          oss << '_';
        }
        oss << "true";
        break;
      case ATTRIBUTE_TYPE_VALUE:
        if (!oss.str().empty()) {
          oss << '_';
        }
        oss << it->str_;
        break;
    }
  }
  if (oss.str().empty()) {
    return;
  }

  attributes.upsert("id", attribute_value(oss.str()));
}

static void
delete_falsey_values(attributes_type& m)
{
  for (attributes_type::iterator it = m.begin(); it != m.end();) {
    if (it->second.type_ == ATTRIBUTE_TYPE_FALSE) {
      attributes_type::iterator jt = it;
      ++it;
      m.erase(jt);
    } else {
      ++it;
    }
  }
}

static attributes_type
merge(VALUE object_ref, int argc, VALUE *argv)
{
  int i;
  attribute_holder attributes;

  for (i = 0; i < argc; i++) {
    merge_one(attributes, argv[i]);
  }
  if (!NIL_P(object_ref)) {
    merge_one(attributes, object_ref);
  }

  join_class_attribute(attributes);
  join_id_attribute(attributes);
  delete_falsey_values(attributes.m_);

  return attributes.m_;
}

static void
put_attribute(std::ostringstream& oss, const std::string& attr_quote, const std::string& key, const std::string& value)
{
  oss << " " << key << "=" << attr_quote;

  gh_buf ob = GH_BUF_INIT;
  if (houdini_escape_html0(&ob, (const uint8_t *)value.data(), value.size(), 0)) {
    oss << std::string(ob.ptr, ob.size);
    gh_buf_free(&ob);
  } else {
    oss << value;
  }
  oss << attr_quote;
}

static void
build_attribute(std::ostringstream& oss, const std::string& attr_quote, int is_html, const std::string& key, const attribute_value& value)
{
  if (value.type_ == ATTRIBUTE_TYPE_TRUE) {
    if (is_html) {
      oss << ' ' << key;
    } else {
      put_attribute(oss, attr_quote, key, key);
    }
  } else {
    put_attribute(oss, attr_quote, key, value.str_);
  }
}

static VALUE
m_build(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
{
  VALUE object_ref;
  int is_html;

  rb_check_arity(argc, 3, UNLIMITED_ARGUMENTS);
  Check_Type(argv[0], T_STRING);
  const std::string attr_quote = string_from_value(argv[0]);
  is_html = RTEST(argv[1]);
  object_ref = argv[2];
  const attributes_type attributes = merge(object_ref, argc-3, argv+3);

  std::ostringstream oss;
  for (attributes_type::const_iterator it = attributes.begin(); it != attributes.end(); ++it) {
    build_attribute(oss, attr_quote, is_html, it->first, it->second);
  }

  return rb_utf8_str_new(oss.str().data(), oss.str().size());
}

static VALUE
m_normalize_data(RB_UNUSED_VAR(VALUE self), VALUE data)
{
  return to_value(normalize_data(data));
}

static VALUE
m_merge(int argc, VALUE *argv, RB_UNUSED_VAR(VALUE self))
{
  rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
  return to_value(merge(argv[0], argc-1, argv+1));
}

extern "C" {
void
Init_attribute_builder(void)
{
  VALUE mFaml = rb_define_module("Faml");
  rb_mAttributeBuilder = rb_define_module_under(mFaml, "AttributeBuilder");
  rb_define_singleton_method(rb_mAttributeBuilder, "build", RUBY_METHOD_FUNC(m_build), -1);
  rb_define_singleton_method(rb_mAttributeBuilder, "normalize_data", RUBY_METHOD_FUNC(m_normalize_data), 1);
  rb_define_singleton_method(rb_mAttributeBuilder, "merge", RUBY_METHOD_FUNC(m_merge), -1);

  id_flatten = rb_intern("flatten");
  id_hyphen = rb_intern("HYPHEN");
  rb_const_set(rb_mAttributeBuilder, id_hyphen, rb_obj_freeze(rb_str_new_cstr("-")));
}
};
<|endoftext|>


<commit_before>// Copyright (c) 2013 Intel Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "xwalk/extensions/renderer/xwalk_module_system.h"

#include <algorithm>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "v8/include/v8.h"
#include "xwalk/extensions/common/xwalk_extension_switches.h"
#include "xwalk/extensions/renderer/xwalk_extension_client.h"
#include "xwalk/extensions/renderer/xwalk_extension_module.h"

namespace xwalk {
namespace extensions {

namespace {

// Index used to set embedder data into v8::Context, so we can get from a
// context to its corresponding module. Index chosen to not conflict with
// WebCore::V8ContextEmbedderDataField in V8PerContextData.h.
const int kModuleSystemEmbedderDataIndex = 8;

// This is the key used in the data object passed to our callbacks to store a
// pointer back to XWalkExtensionModule.
const char* kXWalkModuleSystem = "kXWalkModuleSystem";

XWalkModuleSystem* GetModuleSystem(
    const v8::FunctionCallbackInfo<v8::Value>& info) {
  v8::HandleScope handle_scope(info.GetIsolate());
  v8::Handle<v8::Object> data = info.Data().As<v8::Object>();
  v8::Handle<v8::Value> module_system =
      data->Get(v8::String::New(kXWalkModuleSystem));
  if (module_system.IsEmpty() || module_system->IsUndefined()) {
    LOG(WARNING) << "Trying to use requireNative from already "
                 << "destroyed module system!";
    return NULL;
  }
  CHECK(module_system->IsExternal());
  return static_cast<XWalkModuleSystem*>(
      module_system.As<v8::External>()->Value());
}

void RequireNativeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  v8::ReturnValue<v8::Value> result(info.GetReturnValue());
  XWalkModuleSystem* module_system = GetModuleSystem(info);
  if (info.Length() < 1) {
    // TODO(cmarcelo): Throw appropriate exception or warning.
    result.SetUndefined();
    return;
  }
  v8::Handle<v8::Object> object =
      module_system->RequireNative(*v8::String::Utf8Value(info[0]));
  if (object.IsEmpty()) {
    // TODO(cmarcelo): Throw appropriate exception or warning.
    result.SetUndefined();
    return;
  }
  result.Set(object);
}

}  // namespace

XWalkModuleSystem::XWalkModuleSystem(v8::Handle<v8::Context> context) {
  v8::Isolate* isolate = context->GetIsolate();
  v8_context_.Reset(isolate, context);

  v8::HandleScope handle_scope(isolate);
  v8::Handle<v8::Object> function_data = v8::Object::New();
  function_data->Set(v8::String::New(kXWalkModuleSystem),
                     v8::External::New(this));
  v8::Handle<v8::FunctionTemplate> require_native_template =
      v8::FunctionTemplate::New(RequireNativeCallback, function_data);

  function_data_.Reset(isolate, function_data);
  require_native_template_.Reset(isolate, require_native_template);
}

XWalkModuleSystem::~XWalkModuleSystem() {
  DeleteExtensionModules();
  STLDeleteValues(&native_modules_);

  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);

  // Deleting the data will disable the functions, they'll return early. We do
  // this because it might be the case that the JS objects we created outlive
  // this object, even if we destroy the references we have.
  // TODO(cmarcelo): Add a test for this case.
  // FIXME(cmarcelo): These calls are causing crashes on shutdown with Chromium
  //                  29.0.1547.57 and had to be commented out.
  // v8::Handle<v8::Object> function_data =
  //     v8::Handle<v8::Object>::New(isolate, function_data_);
  // function_data->Delete(v8::String::New(kXWalkModuleSystem));

  require_native_template_.Dispose();
  require_native_template_.Clear();
  function_data_.Dispose();
  function_data_.Clear();
  v8_context_.Dispose();
  v8_context_.Clear();
}

// static
XWalkModuleSystem* XWalkModuleSystem::GetModuleSystemFromContext(
    v8::Handle<v8::Context> context) {
  return reinterpret_cast<XWalkModuleSystem*>(
      context->GetAlignedPointerFromEmbedderData(
          kModuleSystemEmbedderDataIndex));
}

// static
void XWalkModuleSystem::SetModuleSystemInContext(
    scoped_ptr<XWalkModuleSystem> module_system,
    v8::Handle<v8::Context> context) {
  context->SetAlignedPointerInEmbedderData(kModuleSystemEmbedderDataIndex,
                                           module_system.release());
}

// static
void XWalkModuleSystem::ResetModuleSystemFromContext(
    v8::Handle<v8::Context> context) {
  delete GetModuleSystemFromContext(context);
  SetModuleSystemInContext(scoped_ptr<XWalkModuleSystem>(), context);
}

void XWalkModuleSystem::RegisterExtensionModule(
    scoped_ptr<XWalkExtensionModule> module,
    const std::vector<std::string>& entry_points) {
  const std::string& extension_name = module->extension_name();
  if (ContainsExtensionModule(extension_name)) {
    LOG(WARNING) << "Can't register Extension Module named for extension '"
                 << extension_name << "' in the Module System because name was "
                 << "already registered.";
    return;
  }
  extension_modules_.push_back(
      ExtensionModuleEntry(extension_name, module.release(), entry_points));
}

void XWalkModuleSystem::RegisterNativeModule(
    const std::string& name, scoped_ptr<XWalkNativeModule> module) {
  CHECK(native_modules_.find(name) == native_modules_.end());
  native_modules_[name] = module.release();
}

namespace {

v8::Handle<v8::Value> EnsureTargetObjectForTrampoline(
    v8::Handle<v8::Context> context, const std::vector<std::string>& path,
    std::string* error) {
  v8::Handle<v8::Object> object = context->Global();

  std::vector<std::string>::const_iterator it = path.begin();
  for (; it != path.end(); ++it) {
    v8::Handle<v8::String> part = v8::String::New(it->c_str());
    v8::Handle<v8::Value> value = object->Get(part);

    if (value->IsUndefined()) {
      v8::Handle<v8::Object> next_object = v8::Object::New();
      object->Set(part, next_object);
      object = next_object;
      continue;
    }

    if (!value->IsObject()) {
      *error = "the property '" + *it + "' in the path is undefined";
      return v8::Undefined();
    }

    object = value.As<v8::Object>();
  }
  return object;
}

v8::Handle<v8::Value> GetObjectForPath(v8::Handle<v8::Context> context,
                                       const std::vector<std::string>& path,
                                       std::string* error) {
  v8::Handle<v8::Object> object = context->Global();

  std::vector<std::string>::const_iterator it = path.begin();
  for (; it != path.end(); ++it) {
    v8::Handle<v8::String> part = v8::String::New(it->c_str());
    v8::Handle<v8::Value> value = object->Get(part);

    if (!value->IsObject()) {
      *error = "the property '" + *it + "' in the path is undefined";
      return v8::Undefined();
    }

    object = value.As<v8::Object>();
  }

  return object;
}

}  // namespace

bool XWalkModuleSystem::SetTrampolineAccessorForEntryPoint(
    v8::Handle<v8::Context> context,
    const std::string& entry_point,
    v8::Local<v8::External> user_data) {
  std::vector<std::string> path;
  base::SplitString(entry_point, '.', &path);
  std::string basename = path.back();
  path.pop_back();

  std::string error;
  v8::Handle<v8::Value> value =
      EnsureTargetObjectForTrampoline(context, path, &error);
  if (value->IsUndefined()) {
    LOG(WARNING) << "Error installing trampoline for " << entry_point
                 << ": " << error << ".";
    return false;
  }

  // FIXME(cmarcelo): ensure that trampoline is readonly.
  value.As<v8::Object>()->SetAccessor(v8::String::New(basename.c_str()),
                                      TrampolineCallback, 0, user_data);
  return true;
}

// static
bool XWalkModuleSystem::DeleteAccessorForEntryPoint(
    v8::Handle<v8::Context> context,
    const std::string& entry_point) {
  std::vector<std::string> path;
  base::SplitString(entry_point, '.', &path);
  std::string basename = path.back();
  path.pop_back();

  std::string error;
  v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
  if (value->IsUndefined()) {
    LOG(WARNING) << "Error retrieving object for " << entry_point
                 << ": " << error << ".";
    return false;
  }

  value.As<v8::Object>()->ForceDelete(v8::String::New(basename.c_str()));
  return true;
}

bool XWalkModuleSystem::InstallTrampoline(v8::Handle<v8::Context> context,
                                          ExtensionModuleEntry* entry) {
  v8::Local<v8::External> entry_ptr = v8::External::New(entry);
  bool ret;

  ret = SetTrampolineAccessorForEntryPoint(context, entry->name, entry_ptr);
  if (!ret) {
    LOG(WARNING) << "Error installing trampoline for '"
                 << entry->name << "'.";
    return false;
  }

  std::vector<std::string>::const_iterator it = entry->entry_points.begin();
  for (; it != entry->entry_points.end(); ++it) {
    ret = SetTrampolineAccessorForEntryPoint(context, *it, entry_ptr);
    if (!ret) {
      // TODO(vcgomes): Remove already added trampolines when it fails.
      LOG(WARNING) << "Error installing trampoline for '"
                   << entry->name << "'.";
      return false;
    }
  }

  return true;
}

v8::Handle<v8::Object> XWalkModuleSystem::RequireNative(
    const std::string& name) {
  NativeModuleMap::iterator it = native_modules_.find(name);
  if (it == native_modules_.end())
    return v8::Handle<v8::Object>();
  return it->second->NewInstance();
}

void XWalkModuleSystem::Initialize() {
  CommandLine* cmd_line = CommandLine::ForCurrentProcess();
  const bool on_demand_enabled =
      !cmd_line->HasSwitch(switches::kXWalkDisableLoadingExtensionsOnDemand);

  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);

  v8::Handle<v8::Context> context = GetV8Context();
  v8::Handle<v8::FunctionTemplate> require_native_template =
      v8::Handle<v8::FunctionTemplate>::New(isolate, require_native_template_);
  v8::Handle<v8::Function> require_native =
      require_native_template->GetFunction();

  MarkModulesWithTrampoline();

  ExtensionModules::iterator it = extension_modules_.begin();
  for (; it != extension_modules_.end(); ++it) {
    if (on_demand_enabled && it->use_trampoline) {
      if (InstallTrampoline(context, &*it))
        continue;
      LOG(WARNING) << "Falling back to immediately loading " << it->name;
    }
    it->module->LoadExtensionCode(context, require_native);
  }
}

v8::Handle<v8::Context> XWalkModuleSystem::GetV8Context() {
  return v8::Handle<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
}

bool XWalkModuleSystem::ContainsExtensionModule(const std::string& name) {
  ExtensionModules::iterator it = extension_modules_.begin();
  for (; it != extension_modules_.end(); ++it) {
    if (it->name == name)
      return true;
  }
  return false;
}

void XWalkModuleSystem::DeleteExtensionModules() {
  for (ExtensionModules::iterator it = extension_modules_.begin();
       it != extension_modules_.end(); ++it) {
    delete it->module;
  }
  extension_modules_.clear();
}

// static
void XWalkModuleSystem::TrampolineCallback(
    v8::Local<v8::String> property,
    const v8::PropertyCallbackInfo<v8::Value>& info) {
  void* ptr = info.Data().As<v8::External>()->Value();
  ExtensionModuleEntry* entry = static_cast<ExtensionModuleEntry*>(ptr);

  if (!entry)
    return;

  v8::Isolate* isolate = info.GetIsolate();
  v8::Handle<v8::Context> context = isolate->GetCurrentContext();

  DeleteAccessorForEntryPoint(context, entry->name);

  std::vector<std::string>::const_iterator it = entry->entry_points.begin();
  for (; it != entry->entry_points.end(); ++it) {
    DeleteAccessorForEntryPoint(context, *it);
  }

  XWalkModuleSystem* module_system = GetModuleSystemFromContext(context);
  v8::Handle<v8::FunctionTemplate> require_native_template =
        v8::Handle<v8::FunctionTemplate>::New(
            isolate,
            module_system->require_native_template_);

  XWalkExtensionModule* module = entry->module;
  module->LoadExtensionCode(module_system->GetV8Context(),
                            require_native_template->GetFunction());

  v8::Handle<v8::Object> holder = info.Holder();
  info.GetReturnValue().Set(holder->Get(property));
}

XWalkModuleSystem::ExtensionModuleEntry::ExtensionModuleEntry(
  const std::string& name,
  XWalkExtensionModule* module,
  const std::vector<std::string>& entry_points) :
    name(name), module(module), use_trampoline(true),
    entry_points(entry_points) {
}

XWalkModuleSystem::ExtensionModuleEntry::~ExtensionModuleEntry() {
}

// Returns whether the name of first is prefix of the second, considering "."
// character as a separator. So "a" is prefix of "a.b" but not of "ab".
bool XWalkModuleSystem::ExtensionModuleEntry::IsPrefix(
    const ExtensionModuleEntry& first,
    const ExtensionModuleEntry& second) {
  const std::string& p = first.name;
  const std::string& s = second.name;
  return s.size() > p.size() && s[p.size()] == '.'
      && std::mismatch(p.begin(), p.end(), s.begin()).first == p.end();
}

// Mark the extension modules that we want to setup "trampolines"
// instead of loading the code directly. The current algorithm is very
// simple: we only create trampolines for extensions that are leaves
// in the namespace tree.
//
// For example, if there are two extensions "tizen" and "tizen.time",
// the first one won't be marked with trampoline, but the second one
// will. So we'll only load code for "tizen" extension.
void XWalkModuleSystem::MarkModulesWithTrampoline() {
  std::sort(extension_modules_.begin(), extension_modules_.end());

  ExtensionModules::iterator it = extension_modules_.begin();
  while (it != extension_modules_.end()) {
    it = std::adjacent_find(it, extension_modules_.end(),
                            &ExtensionModuleEntry::IsPrefix);
    if (it == extension_modules_.end())
      break;
    VLOG(0) << it->name << " should not use trampoline.";
    it->use_trampoline = false;
    ++it;
  }
}

}  // namespace extensions
}  // namespace xwalk
<commit_msg>[Extensions] Remove development debug statements<commit_after>// Copyright (c) 2013 Intel Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "xwalk/extensions/renderer/xwalk_module_system.h"

#include <algorithm>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "v8/include/v8.h"
#include "xwalk/extensions/common/xwalk_extension_switches.h"
#include "xwalk/extensions/renderer/xwalk_extension_client.h"
#include "xwalk/extensions/renderer/xwalk_extension_module.h"

namespace xwalk {
namespace extensions {

namespace {

// Index used to set embedder data into v8::Context, so we can get from a
// context to its corresponding module. Index chosen to not conflict with
// WebCore::V8ContextEmbedderDataField in V8PerContextData.h.
const int kModuleSystemEmbedderDataIndex = 8;

// This is the key used in the data object passed to our callbacks to store a
// pointer back to XWalkExtensionModule.
const char* kXWalkModuleSystem = "kXWalkModuleSystem";

XWalkModuleSystem* GetModuleSystem(
    const v8::FunctionCallbackInfo<v8::Value>& info) {
  v8::HandleScope handle_scope(info.GetIsolate());
  v8::Handle<v8::Object> data = info.Data().As<v8::Object>();
  v8::Handle<v8::Value> module_system =
      data->Get(v8::String::New(kXWalkModuleSystem));
  if (module_system.IsEmpty() || module_system->IsUndefined()) {
    LOG(WARNING) << "Trying to use requireNative from already "
                 << "destroyed module system!";
    return NULL;
  }
  CHECK(module_system->IsExternal());
  return static_cast<XWalkModuleSystem*>(
      module_system.As<v8::External>()->Value());
}

void RequireNativeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  v8::ReturnValue<v8::Value> result(info.GetReturnValue());
  XWalkModuleSystem* module_system = GetModuleSystem(info);
  if (info.Length() < 1) {
    // TODO(cmarcelo): Throw appropriate exception or warning.
    result.SetUndefined();
    return;
  }
  v8::Handle<v8::Object> object =
      module_system->RequireNative(*v8::String::Utf8Value(info[0]));
  if (object.IsEmpty()) {
    // TODO(cmarcelo): Throw appropriate exception or warning.
    result.SetUndefined();
    return;
  }
  result.Set(object);
}

}  // namespace

XWalkModuleSystem::XWalkModuleSystem(v8::Handle<v8::Context> context) {
  v8::Isolate* isolate = context->GetIsolate();
  v8_context_.Reset(isolate, context);

  v8::HandleScope handle_scope(isolate);
  v8::Handle<v8::Object> function_data = v8::Object::New();
  function_data->Set(v8::String::New(kXWalkModuleSystem),
                     v8::External::New(this));
  v8::Handle<v8::FunctionTemplate> require_native_template =
      v8::FunctionTemplate::New(RequireNativeCallback, function_data);

  function_data_.Reset(isolate, function_data);
  require_native_template_.Reset(isolate, require_native_template);
}

XWalkModuleSystem::~XWalkModuleSystem() {
  DeleteExtensionModules();
  STLDeleteValues(&native_modules_);

  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);

  // Deleting the data will disable the functions, they'll return early. We do
  // this because it might be the case that the JS objects we created outlive
  // this object, even if we destroy the references we have.
  // TODO(cmarcelo): Add a test for this case.
  // FIXME(cmarcelo): These calls are causing crashes on shutdown with Chromium
  //                  29.0.1547.57 and had to be commented out.
  // v8::Handle<v8::Object> function_data =
  //     v8::Handle<v8::Object>::New(isolate, function_data_);
  // function_data->Delete(v8::String::New(kXWalkModuleSystem));

  require_native_template_.Dispose();
  require_native_template_.Clear();
  function_data_.Dispose();
  function_data_.Clear();
  v8_context_.Dispose();
  v8_context_.Clear();
}

// static
XWalkModuleSystem* XWalkModuleSystem::GetModuleSystemFromContext(
    v8::Handle<v8::Context> context) {
  return reinterpret_cast<XWalkModuleSystem*>(
      context->GetAlignedPointerFromEmbedderData(
          kModuleSystemEmbedderDataIndex));
}

// static
void XWalkModuleSystem::SetModuleSystemInContext(
    scoped_ptr<XWalkModuleSystem> module_system,
    v8::Handle<v8::Context> context) {
  context->SetAlignedPointerInEmbedderData(kModuleSystemEmbedderDataIndex,
                                           module_system.release());
}

// static
void XWalkModuleSystem::ResetModuleSystemFromContext(
    v8::Handle<v8::Context> context) {
  delete GetModuleSystemFromContext(context);
  SetModuleSystemInContext(scoped_ptr<XWalkModuleSystem>(), context);
}

void XWalkModuleSystem::RegisterExtensionModule(
    scoped_ptr<XWalkExtensionModule> module,
    const std::vector<std::string>& entry_points) {
  const std::string& extension_name = module->extension_name();
  if (ContainsExtensionModule(extension_name)) {
    LOG(WARNING) << "Can't register Extension Module named for extension '"
                 << extension_name << "' in the Module System because name was "
                 << "already registered.";
    return;
  }
  extension_modules_.push_back(
      ExtensionModuleEntry(extension_name, module.release(), entry_points));
}

void XWalkModuleSystem::RegisterNativeModule(
    const std::string& name, scoped_ptr<XWalkNativeModule> module) {
  CHECK(native_modules_.find(name) == native_modules_.end());
  native_modules_[name] = module.release();
}

namespace {

v8::Handle<v8::Value> EnsureTargetObjectForTrampoline(
    v8::Handle<v8::Context> context, const std::vector<std::string>& path,
    std::string* error) {
  v8::Handle<v8::Object> object = context->Global();

  std::vector<std::string>::const_iterator it = path.begin();
  for (; it != path.end(); ++it) {
    v8::Handle<v8::String> part = v8::String::New(it->c_str());
    v8::Handle<v8::Value> value = object->Get(part);

    if (value->IsUndefined()) {
      v8::Handle<v8::Object> next_object = v8::Object::New();
      object->Set(part, next_object);
      object = next_object;
      continue;
    }

    if (!value->IsObject()) {
      *error = "the property '" + *it + "' in the path is undefined";
      return v8::Undefined();
    }

    object = value.As<v8::Object>();
  }
  return object;
}

v8::Handle<v8::Value> GetObjectForPath(v8::Handle<v8::Context> context,
                                       const std::vector<std::string>& path,
                                       std::string* error) {
  v8::Handle<v8::Object> object = context->Global();

  std::vector<std::string>::const_iterator it = path.begin();
  for (; it != path.end(); ++it) {
    v8::Handle<v8::String> part = v8::String::New(it->c_str());
    v8::Handle<v8::Value> value = object->Get(part);

    if (!value->IsObject()) {
      *error = "the property '" + *it + "' in the path is undefined";
      return v8::Undefined();
    }

    object = value.As<v8::Object>();
  }

  return object;
}

}  // namespace

bool XWalkModuleSystem::SetTrampolineAccessorForEntryPoint(
    v8::Handle<v8::Context> context,
    const std::string& entry_point,
    v8::Local<v8::External> user_data) {
  std::vector<std::string> path;
  base::SplitString(entry_point, '.', &path);
  std::string basename = path.back();
  path.pop_back();

  std::string error;
  v8::Handle<v8::Value> value =
      EnsureTargetObjectForTrampoline(context, path, &error);
  if (value->IsUndefined()) {
    LOG(WARNING) << "Error installing trampoline for " << entry_point
                 << ": " << error << ".";
    return false;
  }

  // FIXME(cmarcelo): ensure that trampoline is readonly.
  value.As<v8::Object>()->SetAccessor(v8::String::New(basename.c_str()),
                                      TrampolineCallback, 0, user_data);
  return true;
}

// static
bool XWalkModuleSystem::DeleteAccessorForEntryPoint(
    v8::Handle<v8::Context> context,
    const std::string& entry_point) {
  std::vector<std::string> path;
  base::SplitString(entry_point, '.', &path);
  std::string basename = path.back();
  path.pop_back();

  std::string error;
  v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
  if (value->IsUndefined()) {
    LOG(WARNING) << "Error retrieving object for " << entry_point
                 << ": " << error << ".";
    return false;
  }

  value.As<v8::Object>()->ForceDelete(v8::String::New(basename.c_str()));
  return true;
}

bool XWalkModuleSystem::InstallTrampoline(v8::Handle<v8::Context> context,
                                          ExtensionModuleEntry* entry) {
  v8::Local<v8::External> entry_ptr = v8::External::New(entry);
  bool ret;

  ret = SetTrampolineAccessorForEntryPoint(context, entry->name, entry_ptr);
  if (!ret) {
    LOG(WARNING) << "Error installing trampoline for '"
                 << entry->name << "'.";
    return false;
  }

  std::vector<std::string>::const_iterator it = entry->entry_points.begin();
  for (; it != entry->entry_points.end(); ++it) {
    ret = SetTrampolineAccessorForEntryPoint(context, *it, entry_ptr);
    if (!ret) {
      // TODO(vcgomes): Remove already added trampolines when it fails.
      LOG(WARNING) << "Error installing trampoline for '"
                   << entry->name << "'.";
      return false;
    }
  }

  return true;
}

v8::Handle<v8::Object> XWalkModuleSystem::RequireNative(
    const std::string& name) {
  NativeModuleMap::iterator it = native_modules_.find(name);
  if (it == native_modules_.end())
    return v8::Handle<v8::Object>();
  return it->second->NewInstance();
}

void XWalkModuleSystem::Initialize() {
  CommandLine* cmd_line = CommandLine::ForCurrentProcess();
  const bool on_demand_enabled =
      !cmd_line->HasSwitch(switches::kXWalkDisableLoadingExtensionsOnDemand);

  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(isolate);

  v8::Handle<v8::Context> context = GetV8Context();
  v8::Handle<v8::FunctionTemplate> require_native_template =
      v8::Handle<v8::FunctionTemplate>::New(isolate, require_native_template_);
  v8::Handle<v8::Function> require_native =
      require_native_template->GetFunction();

  MarkModulesWithTrampoline();

  ExtensionModules::iterator it = extension_modules_.begin();
  for (; it != extension_modules_.end(); ++it) {
    if (on_demand_enabled && it->use_trampoline) {
      if (InstallTrampoline(context, &*it))
        continue;
    }
    it->module->LoadExtensionCode(context, require_native);
  }
}

v8::Handle<v8::Context> XWalkModuleSystem::GetV8Context() {
  return v8::Handle<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
}

bool XWalkModuleSystem::ContainsExtensionModule(const std::string& name) {
  ExtensionModules::iterator it = extension_modules_.begin();
  for (; it != extension_modules_.end(); ++it) {
    if (it->name == name)
      return true;
  }
  return false;
}

void XWalkModuleSystem::DeleteExtensionModules() {
  for (ExtensionModules::iterator it = extension_modules_.begin();
       it != extension_modules_.end(); ++it) {
    delete it->module;
  }
  extension_modules_.clear();
}

// static
void XWalkModuleSystem::TrampolineCallback(
    v8::Local<v8::String> property,
    const v8::PropertyCallbackInfo<v8::Value>& info) {
  void* ptr = info.Data().As<v8::External>()->Value();
  ExtensionModuleEntry* entry = static_cast<ExtensionModuleEntry*>(ptr);

  if (!entry)
    return;

  v8::Isolate* isolate = info.GetIsolate();
  v8::Handle<v8::Context> context = isolate->GetCurrentContext();

  DeleteAccessorForEntryPoint(context, entry->name);

  std::vector<std::string>::const_iterator it = entry->entry_points.begin();
  for (; it != entry->entry_points.end(); ++it) {
    DeleteAccessorForEntryPoint(context, *it);
  }

  XWalkModuleSystem* module_system = GetModuleSystemFromContext(context);
  v8::Handle<v8::FunctionTemplate> require_native_template =
        v8::Handle<v8::FunctionTemplate>::New(
            isolate,
            module_system->require_native_template_);

  XWalkExtensionModule* module = entry->module;
  module->LoadExtensionCode(module_system->GetV8Context(),
                            require_native_template->GetFunction());

  v8::Handle<v8::Object> holder = info.Holder();
  info.GetReturnValue().Set(holder->Get(property));
}

XWalkModuleSystem::ExtensionModuleEntry::ExtensionModuleEntry(
  const std::string& name,
  XWalkExtensionModule* module,
  const std::vector<std::string>& entry_points) :
    name(name), module(module), use_trampoline(true),
    entry_points(entry_points) {
}

XWalkModuleSystem::ExtensionModuleEntry::~ExtensionModuleEntry() {
}

// Returns whether the name of first is prefix of the second, considering "."
// character as a separator. So "a" is prefix of "a.b" but not of "ab".
bool XWalkModuleSystem::ExtensionModuleEntry::IsPrefix(
    const ExtensionModuleEntry& first,
    const ExtensionModuleEntry& second) {
  const std::string& p = first.name;
  const std::string& s = second.name;
  return s.size() > p.size() && s[p.size()] == '.'
      && std::mismatch(p.begin(), p.end(), s.begin()).first == p.end();
}

// Mark the extension modules that we want to setup "trampolines"
// instead of loading the code directly. The current algorithm is very
// simple: we only create trampolines for extensions that are leaves
// in the namespace tree.
//
// For example, if there are two extensions "tizen" and "tizen.time",
// the first one won't be marked with trampoline, but the second one
// will. So we'll only load code for "tizen" extension.
void XWalkModuleSystem::MarkModulesWithTrampoline() {
  std::sort(extension_modules_.begin(), extension_modules_.end());

  ExtensionModules::iterator it = extension_modules_.begin();
  while (it != extension_modules_.end()) {
    it = std::adjacent_find(it, extension_modules_.end(),
                            &ExtensionModuleEntry::IsPrefix);
    if (it == extension_modules_.end())
      break;
    it->use_trampoline = false;
    ++it;
  }
}

}  // namespace extensions
}  // namespace xwalk
<|endoftext|>


<commit_before>/*
 *   CrissCross
 *   A multi-purpose cross-platform library.
 *
 *   A product of Uplink Laboratories.
 *
 *   (c) 2006-2010 Steven Noonan.
 *   Licensed under the New BSD License.
 *
 */

#include <crisscross/universal_include.h>

#include <cstdio>
#include <cstring>

#include <crisscross/core_socket.h>
#include <crisscross/tcpsocket.h>

/* We're leaving sockets unimplemented on the Nintendo DS for the moment. We
 * need to familiarize ourselves with the devkitARM API for sockets first */
#if !defined (TARGET_OS_NDSFIRMWARE)

#if !defined (TARGET_OS_WINDOWS)
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <signal.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#else
#include <winsock2.h>
#include <mstcpip.h>
typedef int socklen_t;
#endif

namespace CrissCross
{
	namespace Network
	{
		TCPSocket::TCPSocket() : CoreSocket()
		{
			m_proto = PROTOCOL_TCP;
		}

		TCPSocket::TCPSocket(socket_t _socket) : CoreSocket(_socket)
		{
			m_proto = PROTOCOL_TCP;
			m_state = SOCKET_STATE_CONNECTED;             /* Assumed. */
		}

		TCPSocket::~TCPSocket()
		{
		}

		int TCPSocket::Accept(TCPSocket * *_socket)
		{
			CoreAssert(this != NULL);

			/* We're forced to accept any incoming connection, unfortunately. */
			socket_t sock = accept(m_sock, 0, 0);

			/* Did we truly accept a connection? */
			if (sock != INVALID_SOCKET) {
				/* Set up the typical transmission attributes. */
				SetAttributes(sock);

#if 0
				unsigned long arg = 1;

				/* Non-blocking I/O, if possible. Ignore any errors. */
#if defined (TARGET_OS_WINDOWS)
				ioctlsocket(m_sock, FIONBIO, &arg);
#else
				ioctl(m_sock, FIONBIO, &arg);
#endif
#endif

				/* Create a new wrapper for our socket. */
				TCPSocket *csock = new TCPSocket(sock);

				m_state = SOCKET_STATE_CONNECTED;

				/* We're done. */
				*_socket = csock;
				return CC_ERR_NONE;
			}

			/* Nothing accepted. */
			*_socket = NULL;
			return CC_ERR_NO_SOCK;
		}

		int TCPSocket::Connect(const char *_address, unsigned short _port)
		{
			CoreAssert(this != NULL);

			struct sockaddr_in sin;
			struct hostent *host;

			/* Close any existing connections on this socket. */
			Close();

			/* Open a new TCP/IP socket. */
			m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

			/* Verify the socket. */
			if (m_sock == INVALID_SOCKET)
				return GetError();

			/* Set up the typical transmission attributes. */
			/* SetAttributes ( m_sock ); */

#if 0
			unsigned long arg = 1;

			/* Non-blocking I/O, if possible. Ignore any errors. */
#if defined (TARGET_OS_WINDOWS)
			ioctlsocket(m_sock, FIONBIO, &arg);
#else
			ioctl(m_sock, FIONBIO, &arg);
#endif
#endif

			/* Resolve the IP of the host we're trying to connect to. */
			host = gethostbyname((char *)_address);
			if (!host) return GetError();

			/* Set up our sockaddr_in. */
			memset(&sin, 0, sizeof(sin));
			sin.sin_family = AF_INET;
			sin.sin_addr.s_addr = (( struct in_addr * )(host->h_addr))->s_addr;
			sin.sin_port = htons(_port);

			/* Attempt a connection. */
			if (connect(m_sock, (( struct sockaddr * )&sin), sizeof(sin)) != 0) {
				int err = GetError();

				/* If this is a non-blocking socket, we need to handle appropriately. */
				if (err == CC_ERR_WOULD_BLOCK) {
					m_state = SOCKET_STATE_CONNECTING;
					return CC_ERR_WOULD_BLOCK;
				} else {
					m_state = SOCKET_STATE_ERROR;
					/* Close the connection, it failed. */
#ifdef TARGET_OS_WINDOWS
					closesocket(m_sock);
#else
					close(m_sock);
#endif
					return err;
				}
			}

			m_state = SOCKET_STATE_CONNECTED;
			return CC_ERR_NONE;
		}

		int TCPSocket::Listen(unsigned short _port)
		{
			CoreAssert(this != NULL);

			struct sockaddr_in sin;

			/* Verify our socket isn't in use. */
			if (m_sock != INVALID_SOCKET) return CC_ERR_INVALID_CALL;

			/* Set up our sockaddr_in */
			memset(&sin, 0, sizeof(sin));
			sin.sin_family = PF_INET;
			sin.sin_addr.s_addr = INADDR_ANY;
			sin.sin_port = htons(_port);

			/* Open a new TCP/IP socket. */
			m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

			/* Verify the socket. */
			if (m_sock == INVALID_SOCKET)
				return GetError();

			/* Set up the typical transmission attributes. */
			SetAttributes(m_sock);

#if 0
			unsigned long arg = 1;

			/* Non-blocking I/O, if possible. Ignore any errors. */
#if defined (TARGET_OS_WINDOWS)
			ioctlsocket(m_sock, FIONBIO, &arg);
#else
			ioctl(m_sock, FIONBIO, &arg);
#endif
#endif

			/* Bind our socket to our given port number. */
			if (bind(m_sock, (sockaddr *)&sin, sizeof(sin)) != 0) {
				/* Bind failure, for some reason. */
				int err = GetError();

#ifdef TARGET_OS_WINDOWS
				closesocket(m_sock);
#else
				close(m_sock);
#endif

				return err;
			}

			/* Listen on the given port, with a maximum of 10 half-open connections. */
			if (listen(m_sock, 10) == SOCKET_ERROR) {
				/* Listen failure, for some reason. */
#ifdef TARGET_OS_WINDOWS
				closesocket(m_sock);
#else
				close(m_sock);
#endif
				return GetError();
			}

			m_state = SOCKET_STATE_LISTENING;
			return CC_ERR_NONE;
		}

		int TCPSocket::SetAttributes(socket_t _socket)
		{
			CoreAssert(this != NULL);

			/* TCP_NODELAY */
			int err, optval = 1, optlen = sizeof optval;
			err = setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
			                 (char *)&optval, optlen);
			if (err == -1) return errno;

			/* SO_LINGER */
			struct linger linger_opts;
			linger_opts.l_onoff = 1;
			linger_opts.l_linger = 10;
			optlen = sizeof linger_opts;
			err = setsockopt(_socket, SOL_SOCKET, SO_LINGER,
			                 (char *)&linger_opts, optlen);
			if (err == -1) return errno;

			/* SO_REUSEADDR */
#ifndef TARGET_OS_WINDOWS
			optval = 1;
			optlen = sizeof(optval);
			setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR,
			           (char *)&optval, optlen);
#endif

			/* SO_KEEPALIVE */
#ifdef TARGET_COMPILER_VC
			DWORD bytesReturned = 0;
			tcp_keepalive vals;
			vals.keepalivetime = 30000;
			vals.keepaliveinterval = 10000;
			vals.onoff = 1;
			err = WSAIoctl(_socket, SIO_KEEPALIVE_VALS,
			               (char *)&vals, sizeof(vals), NULL, 0,
			               &bytesReturned, NULL, NULL);

			if (err == -1) return WSAGetLastError();

#endif
			optlen = sizeof optval;
			err = setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE,
			                 (char *)&optval, optlen);
			if (err == -1) return errno;

			/* SO_SNDTIMEO and SO_RCVTIMEO */
			struct timeval tv;
			tv.tv_sec = 5;
			tv.tv_usec = 0;

			err = setsockopt(_socket, SOL_SOCKET, SO_SNDTIMEO,
			                 (char *)&tv, sizeof(tv));
			if (err == -1) return errno;

			err = setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
			                 (char *)&tv, sizeof(tv));
			if (err == -1) return errno;

			return CC_ERR_NONE;
		}

		socketState TCPSocket::State() const
		{
			CoreAssert(this != NULL);

			/* Make sure there have been no spontaneous state changes. */
			switch (m_state)
			{
			case SOCKET_STATE_CONNECTING:
			{
				if (IsReadable() || IsWritable())
					m_state = SOCKET_STATE_CONNECTED;
			}
			default:
				break;
			}
			return m_state;
		}
	}
}

#endif
<commit_msg>tcpsocket.cpp: fix compile on MinGW<commit_after>/*
 *   CrissCross
 *   A multi-purpose cross-platform library.
 *
 *   A product of Uplink Laboratories.
 *
 *   (c) 2006-2010 Steven Noonan.
 *   Licensed under the New BSD License.
 *
 */

#include <crisscross/universal_include.h>

#include <cstdio>
#include <cstring>

#include <crisscross/core_socket.h>
#include <crisscross/tcpsocket.h>

/* We're leaving sockets unimplemented on the Nintendo DS for the moment. We
 * need to familiarize ourselves with the devkitARM API for sockets first */
#if !defined (TARGET_OS_NDSFIRMWARE)

#if !defined (TARGET_OS_WINDOWS)
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <signal.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#else
#include <winsock2.h>
#ifdef TARGET_COMPILER_VC
#include <mstcpip.h>
#endif
typedef int socklen_t;
#endif

namespace CrissCross
{
	namespace Network
	{
		TCPSocket::TCPSocket() : CoreSocket()
		{
			m_proto = PROTOCOL_TCP;
		}

		TCPSocket::TCPSocket(socket_t _socket) : CoreSocket(_socket)
		{
			m_proto = PROTOCOL_TCP;
			m_state = SOCKET_STATE_CONNECTED;             /* Assumed. */
		}

		TCPSocket::~TCPSocket()
		{
		}

		int TCPSocket::Accept(TCPSocket * *_socket)
		{
			CoreAssert(this != NULL);

			/* We're forced to accept any incoming connection, unfortunately. */
			socket_t sock = accept(m_sock, 0, 0);

			/* Did we truly accept a connection? */
			if (sock != INVALID_SOCKET) {
				/* Set up the typical transmission attributes. */
				SetAttributes(sock);

#if 0
				unsigned long arg = 1;

				/* Non-blocking I/O, if possible. Ignore any errors. */
#if defined (TARGET_OS_WINDOWS)
				ioctlsocket(m_sock, FIONBIO, &arg);
#else
				ioctl(m_sock, FIONBIO, &arg);
#endif
#endif

				/* Create a new wrapper for our socket. */
				TCPSocket *csock = new TCPSocket(sock);

				m_state = SOCKET_STATE_CONNECTED;

				/* We're done. */
				*_socket = csock;
				return CC_ERR_NONE;
			}

			/* Nothing accepted. */
			*_socket = NULL;
			return CC_ERR_NO_SOCK;
		}

		int TCPSocket::Connect(const char *_address, unsigned short _port)
		{
			CoreAssert(this != NULL);

			struct sockaddr_in sin;
			struct hostent *host;

			/* Close any existing connections on this socket. */
			Close();

			/* Open a new TCP/IP socket. */
			m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

			/* Verify the socket. */
			if (m_sock == INVALID_SOCKET)
				return GetError();

			/* Set up the typical transmission attributes. */
			/* SetAttributes ( m_sock ); */

#if 0
			unsigned long arg = 1;

			/* Non-blocking I/O, if possible. Ignore any errors. */
#if defined (TARGET_OS_WINDOWS)
			ioctlsocket(m_sock, FIONBIO, &arg);
#else
			ioctl(m_sock, FIONBIO, &arg);
#endif
#endif

			/* Resolve the IP of the host we're trying to connect to. */
			host = gethostbyname((char *)_address);
			if (!host) return GetError();

			/* Set up our sockaddr_in. */
			memset(&sin, 0, sizeof(sin));
			sin.sin_family = AF_INET;
			sin.sin_addr.s_addr = (( struct in_addr * )(host->h_addr))->s_addr;
			sin.sin_port = htons(_port);

			/* Attempt a connection. */
			if (connect(m_sock, (( struct sockaddr * )&sin), sizeof(sin)) != 0) {
				int err = GetError();

				/* If this is a non-blocking socket, we need to handle appropriately. */
				if (err == CC_ERR_WOULD_BLOCK) {
					m_state = SOCKET_STATE_CONNECTING;
					return CC_ERR_WOULD_BLOCK;
				} else {
					m_state = SOCKET_STATE_ERROR;
					/* Close the connection, it failed. */
#ifdef TARGET_OS_WINDOWS
					closesocket(m_sock);
#else
					close(m_sock);
#endif
					return err;
				}
			}

			m_state = SOCKET_STATE_CONNECTED;
			return CC_ERR_NONE;
		}

		int TCPSocket::Listen(unsigned short _port)
		{
			CoreAssert(this != NULL);

			struct sockaddr_in sin;

			/* Verify our socket isn't in use. */
			if (m_sock != INVALID_SOCKET) return CC_ERR_INVALID_CALL;

			/* Set up our sockaddr_in */
			memset(&sin, 0, sizeof(sin));
			sin.sin_family = PF_INET;
			sin.sin_addr.s_addr = INADDR_ANY;
			sin.sin_port = htons(_port);

			/* Open a new TCP/IP socket. */
			m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

			/* Verify the socket. */
			if (m_sock == INVALID_SOCKET)
				return GetError();

			/* Set up the typical transmission attributes. */
			SetAttributes(m_sock);

#if 0
			unsigned long arg = 1;

			/* Non-blocking I/O, if possible. Ignore any errors. */
#if defined (TARGET_OS_WINDOWS)
			ioctlsocket(m_sock, FIONBIO, &arg);
#else
			ioctl(m_sock, FIONBIO, &arg);
#endif
#endif

			/* Bind our socket to our given port number. */
			if (bind(m_sock, (sockaddr *)&sin, sizeof(sin)) != 0) {
				/* Bind failure, for some reason. */
				int err = GetError();

#ifdef TARGET_OS_WINDOWS
				closesocket(m_sock);
#else
				close(m_sock);
#endif

				return err;
			}

			/* Listen on the given port, with a maximum of 10 half-open connections. */
			if (listen(m_sock, 10) == SOCKET_ERROR) {
				/* Listen failure, for some reason. */
#ifdef TARGET_OS_WINDOWS
				closesocket(m_sock);
#else
				close(m_sock);
#endif
				return GetError();
			}

			m_state = SOCKET_STATE_LISTENING;
			return CC_ERR_NONE;
		}

		int TCPSocket::SetAttributes(socket_t _socket)
		{
			CoreAssert(this != NULL);

			/* TCP_NODELAY */
			int err, optval = 1, optlen = sizeof optval;
			err = setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY,
			                 (char *)&optval, optlen);
			if (err == -1) return errno;

			/* SO_LINGER */
			struct linger linger_opts;
			linger_opts.l_onoff = 1;
			linger_opts.l_linger = 10;
			optlen = sizeof linger_opts;
			err = setsockopt(_socket, SOL_SOCKET, SO_LINGER,
			                 (char *)&linger_opts, optlen);
			if (err == -1) return errno;

			/* SO_REUSEADDR */
#ifndef TARGET_OS_WINDOWS
			optval = 1;
			optlen = sizeof(optval);
			setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR,
			           (char *)&optval, optlen);
#endif

			/* SO_KEEPALIVE */
#ifdef TARGET_COMPILER_VC
			DWORD bytesReturned = 0;
			tcp_keepalive vals;
			vals.keepalivetime = 30000;
			vals.keepaliveinterval = 10000;
			vals.onoff = 1;
			err = WSAIoctl(_socket, SIO_KEEPALIVE_VALS,
			               (char *)&vals, sizeof(vals), NULL, 0,
			               &bytesReturned, NULL, NULL);

			if (err == -1) return WSAGetLastError();

#endif
			optlen = sizeof optval;
			err = setsockopt(_socket, SOL_SOCKET, SO_KEEPALIVE,
			                 (char *)&optval, optlen);
			if (err == -1) return errno;

			/* SO_SNDTIMEO and SO_RCVTIMEO */
			struct timeval tv;
			tv.tv_sec = 5;
			tv.tv_usec = 0;

			err = setsockopt(_socket, SOL_SOCKET, SO_SNDTIMEO,
			                 (char *)&tv, sizeof(tv));
			if (err == -1) return errno;

			err = setsockopt(_socket, SOL_SOCKET, SO_RCVTIMEO,
			                 (char *)&tv, sizeof(tv));
			if (err == -1) return errno;

			return CC_ERR_NONE;
		}

		socketState TCPSocket::State() const
		{
			CoreAssert(this != NULL);

			/* Make sure there have been no spontaneous state changes. */
			switch (m_state)
			{
			case SOCKET_STATE_CONNECTING:
			{
				if (IsReadable() || IsWritable())
					m_state = SOCKET_STATE_CONNECTED;
			}
			default:
				break;
			}
			return m_state;
		}
	}
}

#endif
<|endoftext|>


<commit_before>#include "stdafx.h"

TexMan11 texMan;

namespace Gdiplus {
	using std::min;
	using std::max;
}
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

static ID3D11ShaderResourceView* LoadTextureViaOS(const char* name)
{
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
	WCHAR wc[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wc, dimof(wc));
	Gdiplus::Bitmap* image = new Gdiplus::Bitmap(wc);

	int w = (int)image->GetWidth();
	int h = (int)image->GetHeight();
	Gdiplus::Rect rc(0, 0, w, h);

	Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
	image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);

	std::vector<uint32_t> col;
	col.resize(w * h);
	for (int y = 0; y < h; y++) {
		memcpy(&col[y * w], (char*)bitmapData->Scan0 + bitmapData->Stride * y, w * 4);
		for (int x = 0; x < w; x++) {
			uint32_t& c = col[y * w + x];
			c = (c & 0xff00ff00) | ((c & 0xff) << 16) | ((c & 0xff0000) >> 16);
		}
	}
	image->UnlockBits(bitmapData);
	delete bitmapData;
	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);

	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, w, h, 1, 1, D3D11_BIND_SHADER_RESOURCE);
	D3D11_SUBRESOURCE_DATA r = { &col[0], (uint32_t)w * 4, 0 };
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, &r, &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, nullptr, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

struct DDSHeader {
	uint32_t h3[3];
	uint32_t h, w;
	uint32_t h2[2];
	uint32_t mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2;
	bool IsCubeMap() const { return caps2 == 0xFE00; }
	int GetArraySize() const { return IsCubeMap() ? 6 : 1; }
};

static void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

static ID3D11ShaderResourceView* CreateTextureFromRowDDS(void* img, int size, ivec2& texSize)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	int w = (int)hdr->w;
	int h = (int)hdr->h;
	uint32_t* im = (uint32_t*)img + 128 / 4;
	std::vector<uint32_t> col;
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	int arraySize = hdr->GetArraySize();
	for (int i = 0; i < arraySize; i++) {
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				uint32_t c = *im;
				*im++ = ((hdr->aMask & c) >> aShift << 24) +
					((hdr->bMask & c) >> bShift << 16) +
					((hdr->gMask & c) >> gShift << 8) +
					((hdr->rMask & c) >> rShift);
			}
		}
	}
	texSize.x = hdr->w;
	texSize.y = hdr->h;

	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, w, h, arraySize, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0, 1, 0, hdr->IsCubeMap() ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0);
	CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(hdr->IsCubeMap() ? D3D_SRV_DIMENSION_TEXTURECUBE : D3D_SRV_DIMENSION_TEXTURE2D, desc.Format, 0, 1);
	std::vector<D3D11_SUBRESOURCE_DATA> r;
	int pitch = w * 4;
	int slice = pitch * h;
	for (int i = 0; i < arraySize; i++) {
		r.push_back({ (char*)img + 128 + slice * i, (uint32_t)pitch, 0 });
	}
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, &r[0], &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, &srvDesc, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

static ID3D11ShaderResourceView* LoadDDSTexture(const char* name, ivec2& texSize)
{
	int size;
	ID3D11ShaderResourceView* srv = nullptr;
	void* img = LoadFile(name, &size);
	if (!img) {
		aflog("LoadDDSTexture failed! %s", name);
		return 0;
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	int blockSize = 16;
	DXGI_FORMAT format;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = DXGI_FORMAT_BC1_UNORM;
		blockSize = 8;
		break;
		//	case 0x33545844; //'3TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		//		break;
		//	case 0x35545844; //'5TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		//		break;
	default:
		srv = CreateTextureFromRowDDS(img, size, texSize);
		goto END;
	}
	texSize.x = hdr->w;
	texSize.y = hdr->h;

	{
		int arraySize = hdr->GetArraySize();
		CD3D11_TEXTURE2D_DESC desc(format, hdr->w, hdr->h, arraySize, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0, 1, 0, hdr->IsCubeMap() ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0);
		CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(hdr->IsCubeMap() ? D3D_SRV_DIMENSION_TEXTURECUBE : D3D_SRV_DIMENSION_TEXTURE2D, desc.Format, 0, -1);
		std::vector<D3D11_SUBRESOURCE_DATA> r;
		int pitch = blockSize * ((hdr->w + 3) / 4);
		int slice = pitch * ((hdr->h + 3) / 4);
		for (int i = 0; i < arraySize; i++) {
			r.push_back({ (char*)img + 128 + slice * i, (uint32_t)pitch, 0 });
		}
		ID3D11Texture2D* tex = nullptr;
		deviceMan11.GetDevice()->CreateTexture2D(&desc, &r[0], &tex);
		deviceMan11.GetDevice()->CreateShaderResourceView(tex, &srvDesc, &srv);
		SAFE_RELEASE(tex);
	}
END:
	free(img);
	return srv;
}

TexMan11::TexMan11()
{
	texs.push_back(nullptr);	// make ID 0 invalid
}

TexMan11::~TexMan11()
{
	Destroy();
}

TexMan11::TMID TexMan11::Create(const char *name)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}

	ID3D11ShaderResourceView *tex = nullptr;

	WCHAR wname[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wname, dimof(wname));
	if (!_stricmp(".dds", name + strlen(name) - 4)) {
		ivec2 texSize;
		tex = LoadDDSTexture(name, texSize);
	//	CreateDDSTextureFromFileEx(deviceMan11.GetDevice(), wname, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, nullptr, &tex);
	} else {
		tex = LoadTextureViaOS(name);
	//	CreateWICTextureFromFileEx(deviceMan11.GetDevice(), deviceMan11.GetContext(), wname, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, nullptr, &tex);
	}
	if (!tex) {
		return INVALID_TMID;
	}
	texs.push_back(tex);
	return nameToId[name] = texs.size() - 1;
}

static ID3D11ShaderResourceView* CreateWhiteTexture()
{
	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 1, 1, D3D11_BIND_SHADER_RESOURCE);
	uint32_t white = 0xffffffff;
	D3D11_SUBRESOURCE_DATA r = { &white, 4, 4 };
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, &r, &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, nullptr, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

static ID3D11ShaderResourceView* CreateDynamicTexture(int w, int h)
{
	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, w, h, 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, nullptr, &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, nullptr, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

TexMan11::TMID TexMan11::CreateDynamicTexture(const char* name, int w, int h)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	ID3D11ShaderResourceView* tex = ::CreateDynamicTexture(w, h);
	if (!tex) {
		return INVALID_TMID;
	}
	texs.push_back(tex);
	return nameToId[name] = texs.size() - 1;
}

TexMan11::TMID TexMan11::CreateWhiteTexture()
{
	const std::string name = "$WHITE";
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	ID3D11ShaderResourceView* tex = ::CreateWhiteTexture();
	if (!tex) {
		return INVALID_TMID;
	}
	texs.push_back(tex);
	return nameToId[name] = texs.size() - 1;
}

void TexMan11::Destroy()
{
	for (auto it = texs.begin(); it != texs.end(); it++)
	{
		SAFE_RELEASE(*it);
	}
	texs.clear();
}

ID3D11ShaderResourceView* TexMan11::Get(TMID id)
{
	if (id >= 0 && id < (TMID)texs.size())
	{
		return texs[id];
	}
	return nullptr;
}

ivec2 TexMan11::GetSize(TMID id)
{
	ID3D11ShaderResourceView* view = Get(id);
	assert(view);
	ID3D11Resource* res;
	view->GetResource(&res);
	assert(res);
	ID3D11Texture2D* tx;
	res->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&tx));
	assert(tx);
	SAFE_RELEASE(res);

	D3D11_TEXTURE2D_DESC desc;
	tx->GetDesc(&desc);
	SAFE_RELEASE(tx);

	ivec2 sz;
	sz.x = (int)desc.Width;
	sz.y = (int)desc.Height;

	return sz;
}

void TexMan11::Write(TMID id, const void* buf)
{
	ID3D11ShaderResourceView* view = Get(id);
	assert(view);
	ID3D11Resource* res;
	view->GetResource(&res);
	assert(res);
	ID3D11Texture2D* tx;
	res->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&tx));
	assert(tx);
	SAFE_RELEASE(res);

	D3D11_TEXTURE2D_DESC desc;
	tx->GetDesc(&desc);

	D3D11_MAPPED_SUBRESOURCE m;
	HRESULT hr = deviceMan11.GetContext()->Map(tx, 0, D3D11_MAP_WRITE_DISCARD, 0, &m);
	memcpy(m.pData, buf, desc.Width * desc.Height * 4);
	deviceMan11.GetContext()->Unmap(tx, 0);

	SAFE_RELEASE(tx);
}
<commit_msg>simplify DDS loader<commit_after>#include "stdafx.h"

TexMan11 texMan;

namespace Gdiplus {
	using std::min;
	using std::max;
}
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")

static ID3D11ShaderResourceView* LoadTextureViaOS(const char* name)
{
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
	WCHAR wc[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wc, dimof(wc));
	Gdiplus::Bitmap* image = new Gdiplus::Bitmap(wc);

	int w = (int)image->GetWidth();
	int h = (int)image->GetHeight();
	Gdiplus::Rect rc(0, 0, w, h);

	Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
	image->LockBits(&rc, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);

	std::vector<uint32_t> col;
	col.resize(w * h);
	for (int y = 0; y < h; y++) {
		memcpy(&col[y * w], (char*)bitmapData->Scan0 + bitmapData->Stride * y, w * 4);
		for (int x = 0; x < w; x++) {
			uint32_t& c = col[y * w + x];
			c = (c & 0xff00ff00) | ((c & 0xff) << 16) | ((c & 0xff0000) >> 16);
		}
	}
	image->UnlockBits(bitmapData);
	delete bitmapData;
	delete image;
	Gdiplus::GdiplusShutdown(gdiplusToken);

	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, w, h, 1, 1, D3D11_BIND_SHADER_RESOURCE);
	D3D11_SUBRESOURCE_DATA r = { &col[0], (uint32_t)w * 4, 0 };
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, &r, &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, nullptr, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

struct DDSHeader {
	uint32_t h3[3];
	uint32_t h, w;
	uint32_t h2[2];
	uint32_t mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2;
	bool IsCubeMap() const { return caps2 == 0xFE00; }
	int GetArraySize() const { return IsCubeMap() ? 6 : 1; }
};

static void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

static void ArrangeRawDDS(void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	int w = (int)hdr->w;
	int h = (int)hdr->h;
	uint32_t* im = (uint32_t*)img + 128 / 4;
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	int arraySize = hdr->GetArraySize();
	for (int i = 0; i < arraySize; i++) {
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				uint32_t c = *im;
				*im++ = ((hdr->aMask & c) >> aShift << 24) +
					((hdr->bMask & c) >> bShift << 16) +
					((hdr->gMask & c) >> gShift << 8) +
					((hdr->rMask & c) >> rShift);
			}
		}
	}
}

static ID3D11ShaderResourceView* LoadDDSTexture(const char* name, ivec2& texSize)
{
	int size;
	ID3D11ShaderResourceView* srv = nullptr;
	void* img = LoadFile(name, &size);
	if (!img) {
		aflog("LoadDDSTexture failed! %s", name);
		return 0;
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	int blockSize = 16;
	int pitch = 0, slice = 0;
	DXGI_FORMAT format;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = DXGI_FORMAT_BC1_UNORM;
		blockSize = 8;
		pitch = blockSize * ((hdr->w + 3) / 4);
		slice = pitch * ((hdr->h + 3) / 4);
		break;
		//	case 0x33545844; //'3TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
		//		break;
		//	case 0x35545844; //'5TXD':
		//		format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
		//		break;
	default:
		ArrangeRawDDS(img, size);
		format = DXGI_FORMAT_R8G8B8A8_UNORM;
		pitch = hdr->w * 4;
		slice = pitch * hdr->h;
		break;
	}
	texSize.x = hdr->w;
	texSize.y = hdr->h;

	{
		int arraySize = hdr->GetArraySize();
		CD3D11_TEXTURE2D_DESC desc(format, hdr->w, hdr->h, arraySize, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DEFAULT, 0, 1, 0, hdr->IsCubeMap() ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0);
		CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc(hdr->IsCubeMap() ? D3D_SRV_DIMENSION_TEXTURECUBE : D3D_SRV_DIMENSION_TEXTURE2D, desc.Format, 0, -1);
		std::vector<D3D11_SUBRESOURCE_DATA> r;
		for (int i = 0; i < arraySize; i++) {
			r.push_back({ (char*)img + 128 + slice * i, (uint32_t)pitch, 0 });
		}
		ID3D11Texture2D* tex = nullptr;
		deviceMan11.GetDevice()->CreateTexture2D(&desc, &r[0], &tex);
		deviceMan11.GetDevice()->CreateShaderResourceView(tex, &srvDesc, &srv);
		SAFE_RELEASE(tex);
	}
	free(img);
	return srv;
}

TexMan11::TexMan11()
{
	texs.push_back(nullptr);	// make ID 0 invalid
}

TexMan11::~TexMan11()
{
	Destroy();
}

TexMan11::TMID TexMan11::Create(const char *name)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}

	ID3D11ShaderResourceView *tex = nullptr;

	WCHAR wname[MAX_PATH];
	MultiByteToWideChar(CP_ACP, 0, name, -1, wname, dimof(wname));
	if (!_stricmp(".dds", name + strlen(name) - 4)) {
		ivec2 texSize;
		tex = LoadDDSTexture(name, texSize);
	//	CreateDDSTextureFromFileEx(deviceMan11.GetDevice(), wname, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, nullptr, &tex);
	} else {
		tex = LoadTextureViaOS(name);
	//	CreateWICTextureFromFileEx(deviceMan11.GetDevice(), deviceMan11.GetContext(), wname, 0, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, nullptr, &tex);
	}
	if (!tex) {
		return INVALID_TMID;
	}
	texs.push_back(tex);
	return nameToId[name] = texs.size() - 1;
}

static ID3D11ShaderResourceView* CreateWhiteTexture()
{
	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, 1, 1, D3D11_BIND_SHADER_RESOURCE);
	uint32_t white = 0xffffffff;
	D3D11_SUBRESOURCE_DATA r = { &white, 4, 4 };
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, &r, &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, nullptr, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

static ID3D11ShaderResourceView* CreateDynamicTexture(int w, int h)
{
	CD3D11_TEXTURE2D_DESC desc(DXGI_FORMAT_R8G8B8A8_UNORM, w, h, 1, 1, D3D11_BIND_SHADER_RESOURCE, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
	ID3D11Texture2D* tex = nullptr;
	ID3D11ShaderResourceView* srv = nullptr;
	deviceMan11.GetDevice()->CreateTexture2D(&desc, nullptr, &tex);
	deviceMan11.GetDevice()->CreateShaderResourceView(tex, nullptr, &srv);
	SAFE_RELEASE(tex);
	return srv;
}

TexMan11::TMID TexMan11::CreateDynamicTexture(const char* name, int w, int h)
{
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	ID3D11ShaderResourceView* tex = ::CreateDynamicTexture(w, h);
	if (!tex) {
		return INVALID_TMID;
	}
	texs.push_back(tex);
	return nameToId[name] = texs.size() - 1;
}

TexMan11::TMID TexMan11::CreateWhiteTexture()
{
	const std::string name = "$WHITE";
	auto it = nameToId.find(name);
	if (it != nameToId.end())
	{
		return it->second;
	}
	ID3D11ShaderResourceView* tex = ::CreateWhiteTexture();
	if (!tex) {
		return INVALID_TMID;
	}
	texs.push_back(tex);
	return nameToId[name] = texs.size() - 1;
}

void TexMan11::Destroy()
{
	for (auto it = texs.begin(); it != texs.end(); it++)
	{
		SAFE_RELEASE(*it);
	}
	texs.clear();
}

ID3D11ShaderResourceView* TexMan11::Get(TMID id)
{
	if (id >= 0 && id < (TMID)texs.size())
	{
		return texs[id];
	}
	return nullptr;
}

ivec2 TexMan11::GetSize(TMID id)
{
	ID3D11ShaderResourceView* view = Get(id);
	assert(view);
	ID3D11Resource* res;
	view->GetResource(&res);
	assert(res);
	ID3D11Texture2D* tx;
	res->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&tx));
	assert(tx);
	SAFE_RELEASE(res);

	D3D11_TEXTURE2D_DESC desc;
	tx->GetDesc(&desc);
	SAFE_RELEASE(tx);

	ivec2 sz;
	sz.x = (int)desc.Width;
	sz.y = (int)desc.Height;

	return sz;
}

void TexMan11::Write(TMID id, const void* buf)
{
	ID3D11ShaderResourceView* view = Get(id);
	assert(view);
	ID3D11Resource* res;
	view->GetResource(&res);
	assert(res);
	ID3D11Texture2D* tx;
	res->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&tx));
	assert(tx);
	SAFE_RELEASE(res);

	D3D11_TEXTURE2D_DESC desc;
	tx->GetDesc(&desc);

	D3D11_MAPPED_SUBRESOURCE m;
	HRESULT hr = deviceMan11.GetContext()->Map(tx, 0, D3D11_MAP_WRITE_DISCARD, 0, &m);
	memcpy(m.pData, buf, desc.Width * desc.Height * 4);
	deviceMan11.GetContext()->Unmap(tx, 0);

	SAFE_RELEASE(tx);
}
<|endoftext|>


<commit_before><commit_msg>Add a margin to the screenshot - it looks better that way.<commit_after><|endoftext|>


<commit_before>/* Fault handler information.  MacOSX version.
Copyright (C) 1993-1999, 2002-2003  Bruno Haible <clisp.org at bruno>

Copyright (C) 2003  Paolo Bonzini <gnu.org at bonzini>

Used under BSD license with permission from Paolo Bonzini and Bruno Haible,
2005-03-10:

http://sourceforge.net/mailarchive/message.php?msg_name=200503102200.32002.bruno%40clisp.org

Modified for Factor by Slava Pestov */

#include "master.hpp"

namespace factor
{

/* The exception port on which our thread listens. */
mach_port_t our_exception_port;

/* The following sources were used as a *reference* for this exception handling
code:
1. Apple's mach/xnu documentation
2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
omnigroup's macosx-dev list.
http://www.wodeveloper.com/omniLists/macosx-dev/2000/June/msg00137.html */

/* Modify a suspended thread's thread_state so that when the thread resumes
executing, the call frame of the current C primitive (if any) is rewound, and
the appropriate Factor error is thrown from the top-most Factor frame. */
void factor_vm::call_fault_handler(
	exception_type_t exception,
	exception_data_type_t code,
	MACH_EXC_STATE_TYPE *exc_state,
	MACH_THREAD_STATE_TYPE *thread_state,
	MACH_FLOAT_STATE_TYPE *float_state)
{
	/* There is a race condition here, but in practice an exception
	delivered during stack frame setup/teardown or while transitioning
	from Factor to C is a sign of things seriously gone wrong, not just
	a divide by zero or stack underflow in the listener */

	/* Are we in compiled Factor code? Then use the current stack pointer */
	if(in_code_heap_p(MACH_PROGRAM_COUNTER(thread_state)))
		signal_callstack_top = (stack_frame *)MACH_STACK_POINTER(thread_state);
	/* Are we in C? Then use the saved callstack top */
	else
		signal_callstack_top = NULL;

	MACH_STACK_POINTER(thread_state) = align_stack_pointer(MACH_STACK_POINTER(thread_state));

	/* Now we point the program counter at the right handler function. */
	if(exception == EXC_BAD_ACCESS)
	{
		signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::memory_signal_handler_impl;
	}
	else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV)
	{
		signal_fpu_status = fpu_status(mach_fpu_status(float_state));
		mach_clear_fpu_status(float_state);
		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::fp_signal_handler_impl;
	}
	else
	{
		switch(exception)
		{
		case EXC_ARITHMETIC: signal_number = SIGFPE; break;
		case EXC_BAD_INSTRUCTION: signal_number = SIGILL; break;
		default: signal_number = SIGABRT; break;
		}

		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::misc_signal_handler_impl;
	}
}

static void call_fault_handler(
	mach_port_t thread,
	exception_type_t exception,
	exception_data_type_t code,
	MACH_EXC_STATE_TYPE *exc_state,
	MACH_THREAD_STATE_TYPE *thread_state,
	MACH_FLOAT_STATE_TYPE *float_state)
{
	/* Look up the VM instance involved */
	THREADHANDLE thread_id = pthread_from_mach_thread_np(thread);
	assert(thread_id);
	std::map<THREADHANDLE, factor_vm*>::const_iterator vm = thread_vms.find(thread_id);

	/* Handle the exception */
	if (vm != thread_vms.end())
		vm->second->call_fault_handler(exception,code,exc_state,thread_state,float_state);
}

/* Handle an exception by invoking the user's fault handler and/or forwarding
the duty to the previously installed handlers.	*/
extern "C"
kern_return_t
catch_exception_raise (mach_port_t exception_port,
	mach_port_t thread,
	mach_port_t task,
	exception_type_t exception,
	exception_data_t code,
	mach_msg_type_number_t code_count)
{
	/* 10.6 likes to report exceptions from child processes too. Ignore those */
	if(task != mach_task_self()) return KERN_SUCCESS;

	/* Get fault information and the faulting thread's register contents..

	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html.	*/
	MACH_EXC_STATE_TYPE exc_state;
	mach_msg_type_number_t exc_state_count = MACH_EXC_STATE_COUNT;
	if (thread_get_state (thread, MACH_EXC_STATE_FLAVOR,
			      (natural_t *)&exc_state, &exc_state_count)
		!= KERN_SUCCESS)
	{
		/* The thread is supposed to be suspended while the exception
		handler is called. This shouldn't fail. */
		return KERN_FAILURE;
	}

	MACH_THREAD_STATE_TYPE thread_state;
	mach_msg_type_number_t thread_state_count = MACH_THREAD_STATE_COUNT;
	if (thread_get_state (thread, MACH_THREAD_STATE_FLAVOR,
			      (natural_t *)&thread_state, &thread_state_count)
		!= KERN_SUCCESS)
	{
		/* The thread is supposed to be suspended while the exception
		handler is called. This shouldn't fail. */
		return KERN_FAILURE;
	}

	MACH_FLOAT_STATE_TYPE float_state;
	mach_msg_type_number_t float_state_count = MACH_FLOAT_STATE_COUNT;
	if (thread_get_state (thread, MACH_FLOAT_STATE_FLAVOR,
			      (natural_t *)&float_state, &float_state_count)
		!= KERN_SUCCESS)
	{
		/* The thread is supposed to be suspended while the exception
		handler is called. This shouldn't fail. */
		return KERN_FAILURE;
	}

	/* Modify registers so to have the thread resume executing the
	fault handler */
	call_fault_handler(thread,exception,code[0],&exc_state,&thread_state,&float_state);

	/* Set the faulting thread's register contents..

	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html.	*/
	if (thread_set_state (thread, MACH_FLOAT_STATE_FLAVOR,
			      (natural_t *)&float_state, float_state_count)
		!= KERN_SUCCESS)
	{
		return KERN_FAILURE;
	}

	if (thread_set_state (thread, MACH_THREAD_STATE_FLAVOR,
			      (natural_t *)&thread_state, thread_state_count)
		!= KERN_SUCCESS)
	{
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}

/* The main function of the thread listening for exceptions.  */
static void *
mach_exception_thread (void *arg)
{
	for (;;)
	{
		/* These two structures contain some private kernel data. We don't need
		to access any of it so we don't bother defining a proper struct. The
		correct definitions are in the xnu source code. */
		/* Buffer for a message to be received.  */
		struct
		{
			mach_msg_header_t head;
			mach_msg_body_t msgh_body;
			char data[1024];
		}
		msg;
		/* Buffer for a reply message.	*/
		struct
		{
			mach_msg_header_t head;
			char data[1024];
		}
		reply;

		mach_msg_return_t retval;

		/* Wait for a message on the exception port.  */
		retval = mach_msg (&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0,
			sizeof (msg), our_exception_port,
			MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
		if (retval != MACH_MSG_SUCCESS)
		{
			abort ();
		}

		/* Handle the message: Call exc_server, which will call
		catch_exception_raise and produce a reply message.  */
		exc_server (&msg.head, &reply.head);

		/* Send the reply.  */
		if (mach_msg (&reply.head, MACH_SEND_MSG, reply.head.msgh_size,
			0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)
			!= MACH_MSG_SUCCESS)
		{
			abort ();
		}
	}
}

/* Initialize the Mach exception handler thread. */
void mach_initialize ()
{
	mach_port_t self;
	exception_mask_t mask;

	self = mach_task_self ();

	/* Allocate a port on which the thread shall listen for exceptions.  */
	if (mach_port_allocate (self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port)
		!= KERN_SUCCESS)
		fatal_error("mach_port_allocate() failed",0);

	/* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html.  */
	if (mach_port_insert_right (self, our_exception_port, our_exception_port,
		MACH_MSG_TYPE_MAKE_SEND)
		!= KERN_SUCCESS)
		fatal_error("mach_port_insert_right() failed",0);

	/* The exceptions we want to catch. */
	mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC;

	/* Create the thread listening on the exception port.  */
	start_thread(mach_exception_thread,NULL);

	/* Replace the exception port info for these exceptions with our own.
	Note that we replace the exception port for the entire task, not only
	for a particular thread.  This has the effect that when our exception
	port gets the message, the thread specific exception port has already
	been asked, and we don't need to bother about it.
	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html.	*/
	if (task_set_exception_ports (self, mask, our_exception_port,
		EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
		!= KERN_SUCCESS)
		fatal_error("task_set_exception_ports() failed",0);
}

}
<commit_msg>vm: another fix<commit_after>/* Fault handler information.  MacOSX version.
Copyright (C) 1993-1999, 2002-2003  Bruno Haible <clisp.org at bruno>

Copyright (C) 2003  Paolo Bonzini <gnu.org at bonzini>

Used under BSD license with permission from Paolo Bonzini and Bruno Haible,
2005-03-10:

http://sourceforge.net/mailarchive/message.php?msg_name=200503102200.32002.bruno%40clisp.org

Modified for Factor by Slava Pestov */

#include "master.hpp"

namespace factor
{

/* The exception port on which our thread listens. */
mach_port_t our_exception_port;

/* The following sources were used as a *reference* for this exception handling
code:
1. Apple's mach/xnu documentation
2. Timothy J. Wood's "Mach Exception Handlers 101" post to the
omnigroup's macosx-dev list.
http://www.wodeveloper.com/omniLists/macosx-dev/2000/June/msg00137.html */

/* Modify a suspended thread's thread_state so that when the thread resumes
executing, the call frame of the current C primitive (if any) is rewound, and
the appropriate Factor error is thrown from the top-most Factor frame. */
void factor_vm::call_fault_handler(
	exception_type_t exception,
	exception_data_type_t code,
	MACH_EXC_STATE_TYPE *exc_state,
	MACH_THREAD_STATE_TYPE *thread_state,
	MACH_FLOAT_STATE_TYPE *float_state)
{
	/* There is a race condition here, but in practice an exception
	delivered during stack frame setup/teardown or while transitioning
	from Factor to C is a sign of things seriously gone wrong, not just
	a divide by zero or stack underflow in the listener */

	/* Are we in compiled Factor code? Then use the current stack pointer */
	if(in_code_heap_p(MACH_PROGRAM_COUNTER(thread_state)))
		signal_callstack_top = (stack_frame *)MACH_STACK_POINTER(thread_state);
	/* Are we in C? Then use the saved callstack top */
	else
		signal_callstack_top = NULL;

	MACH_STACK_POINTER(thread_state) = align_stack_pointer(MACH_STACK_POINTER(thread_state));

	/* Now we point the program counter at the right handler function. */
	if(exception == EXC_BAD_ACCESS)
	{
		signal_fault_addr = MACH_EXC_STATE_FAULT(exc_state);
		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::memory_signal_handler_impl;
	}
	else if(exception == EXC_ARITHMETIC && code != MACH_EXC_INTEGER_DIV)
	{
		signal_fpu_status = fpu_status(mach_fpu_status(float_state));
		mach_clear_fpu_status(float_state);
		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::fp_signal_handler_impl;
	}
	else
	{
		switch(exception)
		{
		case EXC_ARITHMETIC: signal_number = SIGFPE; break;
		case EXC_BAD_INSTRUCTION: signal_number = SIGILL; break;
		default: signal_number = SIGABRT; break;
		}

		MACH_PROGRAM_COUNTER(thread_state) = (cell)factor::misc_signal_handler_impl;
	}
}

static void call_fault_handler(
	mach_port_t thread,
	exception_type_t exception,
	exception_data_type_t code,
	MACH_EXC_STATE_TYPE *exc_state,
	MACH_THREAD_STATE_TYPE *thread_state,
	MACH_FLOAT_STATE_TYPE *float_state)
{
	/* Look up the VM instance involved */
	THREADHANDLE thread_id = pthread_from_mach_thread_np(thread);
	assert(thread_id);
	std::map<THREADHANDLE, factor_vm*>::const_iterator vm = thread_vms.find(thread_id);

	/* Handle the exception */
	if (vm != thread_vms.end())
		vm->second->call_fault_handler(exception,code,exc_state,thread_state,float_state);
}

/* Handle an exception by invoking the user's fault handler and/or forwarding
the duty to the previously installed handlers.	*/
extern "C"
kern_return_t
catch_exception_raise (mach_port_t exception_port,
	mach_port_t thread,
	mach_port_t task,
	exception_type_t exception,
	exception_data_t code,
	mach_msg_type_number_t code_count)
{
	/* 10.6 likes to report exceptions from child processes too. Ignore those */
	if(task != mach_task_self()) return KERN_FAILURE;

	/* Get fault information and the faulting thread's register contents..

	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_get_state.html.	*/
	MACH_EXC_STATE_TYPE exc_state;
	mach_msg_type_number_t exc_state_count = MACH_EXC_STATE_COUNT;
	if (thread_get_state (thread, MACH_EXC_STATE_FLAVOR,
			      (natural_t *)&exc_state, &exc_state_count)
		!= KERN_SUCCESS)
	{
		/* The thread is supposed to be suspended while the exception
		handler is called. This shouldn't fail. */
		return KERN_FAILURE;
	}

	MACH_THREAD_STATE_TYPE thread_state;
	mach_msg_type_number_t thread_state_count = MACH_THREAD_STATE_COUNT;
	if (thread_get_state (thread, MACH_THREAD_STATE_FLAVOR,
			      (natural_t *)&thread_state, &thread_state_count)
		!= KERN_SUCCESS)
	{
		/* The thread is supposed to be suspended while the exception
		handler is called. This shouldn't fail. */
		return KERN_FAILURE;
	}

	MACH_FLOAT_STATE_TYPE float_state;
	mach_msg_type_number_t float_state_count = MACH_FLOAT_STATE_COUNT;
	if (thread_get_state (thread, MACH_FLOAT_STATE_FLAVOR,
			      (natural_t *)&float_state, &float_state_count)
		!= KERN_SUCCESS)
	{
		/* The thread is supposed to be suspended while the exception
		handler is called. This shouldn't fail. */
		return KERN_FAILURE;
	}

	/* Modify registers so to have the thread resume executing the
	fault handler */
	call_fault_handler(thread,exception,code[0],&exc_state,&thread_state,&float_state);

	/* Set the faulting thread's register contents..

	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/thread_set_state.html.	*/
	if (thread_set_state (thread, MACH_FLOAT_STATE_FLAVOR,
			      (natural_t *)&float_state, float_state_count)
		!= KERN_SUCCESS)
	{
		return KERN_FAILURE;
	}

	if (thread_set_state (thread, MACH_THREAD_STATE_FLAVOR,
			      (natural_t *)&thread_state, thread_state_count)
		!= KERN_SUCCESS)
	{
		return KERN_FAILURE;
	}

	return KERN_SUCCESS;
}

/* The main function of the thread listening for exceptions.  */
static void *
mach_exception_thread (void *arg)
{
	for (;;)
	{
		/* These two structures contain some private kernel data. We don't need
		to access any of it so we don't bother defining a proper struct. The
		correct definitions are in the xnu source code. */
		/* Buffer for a message to be received.  */
		struct
		{
			mach_msg_header_t head;
			mach_msg_body_t msgh_body;
			char data[1024];
		}
		msg;
		/* Buffer for a reply message.	*/
		struct
		{
			mach_msg_header_t head;
			char data[1024];
		}
		reply;

		mach_msg_return_t retval;

		/* Wait for a message on the exception port.  */
		retval = mach_msg (&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0,
			sizeof (msg), our_exception_port,
			MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
		if (retval != MACH_MSG_SUCCESS)
		{
			abort ();
		}

		/* Handle the message: Call exc_server, which will call
		catch_exception_raise and produce a reply message.  */
		exc_server (&msg.head, &reply.head);

		/* Send the reply.  */
		if (mach_msg (&reply.head, MACH_SEND_MSG, reply.head.msgh_size,
			0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL)
			!= MACH_MSG_SUCCESS)
		{
			abort ();
		}
	}
}

/* Initialize the Mach exception handler thread. */
void mach_initialize ()
{
	mach_port_t self;
	exception_mask_t mask;

	self = mach_task_self ();

	/* Allocate a port on which the thread shall listen for exceptions.  */
	if (mach_port_allocate (self, MACH_PORT_RIGHT_RECEIVE, &our_exception_port)
		!= KERN_SUCCESS)
		fatal_error("mach_port_allocate() failed",0);

	/* See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html.  */
	if (mach_port_insert_right (self, our_exception_port, our_exception_port,
		MACH_MSG_TYPE_MAKE_SEND)
		!= KERN_SUCCESS)
		fatal_error("mach_port_insert_right() failed",0);

	/* The exceptions we want to catch. */
	mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC;

	/* Create the thread listening on the exception port.  */
	start_thread(mach_exception_thread,NULL);

	/* Replace the exception port info for these exceptions with our own.
	Note that we replace the exception port for the entire task, not only
	for a particular thread.  This has the effect that when our exception
	port gets the message, the thread specific exception port has already
	been asked, and we don't need to bother about it.
	See http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html.	*/
	if (task_set_exception_ports (self, mask, our_exception_port,
		EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
		!= KERN_SUCCESS)
		fatal_error("task_set_exception_ports() failed",0);
}

}
<|endoftext|>