[aseprite] 153/196: Refactor scripting code

Tobias Hansen thansen at moszumanska.debian.org
Wed Apr 20 18:50:14 UTC 2016


This is an automated email from the git hooks/post-receive script.

thansen pushed a commit to branch master
in repository aseprite.

commit 21921fdfd53d54e0e76f3fe224a16bff0b9d0ca6
Author: David Capello <davidcapello at gmail.com>
Date:   Wed Apr 6 15:37:13 2016 -0300

    Refactor scripting code
---
 README.md                                     |   3 +-
 data/scripts/white_to_alpha.js                |  25 +-
 src/CMakeLists.txt                            |   2 +-
 src/app/CMakeLists.txt                        |   7 +-
 src/app/app.cpp                               |   6 +-
 src/app/commands/cmd_run_script.cpp           |   8 +-
 src/app/script/app_object.cpp                 |  73 ++++
 src/app/{shell.h => script/app_object.h}      |  18 +-
 src/app/script/app_scripting.cpp              |  32 ++
 src/app/{scripting => script}/app_scripting.h |  13 +-
 src/app/script/console_object.cpp             |  65 ++++
 src/app/{shell.h => script/console_object.h}  |  18 +-
 src/app/script/pixel_color.h                  |  86 +++++
 src/app/script/sprite_class.cpp               | 227 ++++++++++++
 src/app/script/sprite_class.h                 |  24 ++
 src/app/scripting/app_scripting.cpp           |  40 --
 src/app/scripting/raw_color.h                 |  49 ---
 src/app/scripting/sprite.h                    | 143 --------
 src/app/shell.cpp                             |   6 +-
 src/app/shell.h                               |   6 +-
 src/app/ui/devconsole_view.h                  |   8 +-
 src/script/CMakeLists.txt                     |   9 +
 src/{scripting => script}/LICENSE.txt         |   2 +-
 src/{scripting => script}/README.md           |   2 +-
 src/script/engine.cpp                         | 507 ++++++++++++++++++++++++++
 src/{scripting => script}/engine.h            |  94 +++--
 src/{scripting => script}/engine_delegate.h   |   8 +-
 src/scripting/CMakeLists.txt                  |   7 -
 src/scripting/engine.cpp                      | 356 ------------------
 third_party/duktape                           |   2 +-
 30 files changed, 1155 insertions(+), 691 deletions(-)

diff --git a/README.md b/README.md
index ff6ba2e..9e96adb 100644
--- a/README.md
+++ b/README.md
@@ -82,16 +82,17 @@ And it uses the following third-party libraries:
 * [Google Test](https://github.com/google/googletest) - [gtest license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/gtest-LICENSE.txt)
 * [XFree86](http://www.x.org/) - [XFree86 license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/XFree86-LICENSE.txt)
 * [curl](http://curl.haxx.se/) - [curl license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/curl-LICENSE.txt)
+* [duktape](http://duktape.org/) - [MIT license](https://github.com/aseprite/aseprite/tree/master/third_party/duktape/LICENSE.txt)
 * [giflib](http://sourceforge.net/projects/giflib/) - [giflib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/giflib-LICENSE.txt)
 * [libjpeg](http://www.ijg.org/) - [libjpeg license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/libjpeg-LICENSE.txt)
 * [libpng](http://www.libpng.org/pub/png/) - [libpng license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/libpng-LICENSE.txt)
 * [libwebp](https://developers.google.com/speed/webp/) - [libwebp license](https://chromium.googlesource.com/webm/libwebp/+/master/COPYING)
 * [loadpng](http://tjaden.strangesoft.net/loadpng/) - [zlib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/ZLIB.txt)
+* [modp_b64](https://github.com/aseprite/aseprite/tree/master/third_party/modp_b64/modp_b64.h) - [BSD license](https://github.com/aseprite/aseprite/tree/master/third_party/modp_b64/LICENSE)
 * [pixman](http://www.pixman.org/) - [MIT license](http://cgit.freedesktop.org/pixman/plain/COPYING)
 * [simpleini](https://github.com/aseprite/simpleini/) - [MIT license](https://github.com/aseprite/simpleini/blob/aseprite/LICENCE.txt)
 * [tinyxml](http://www.sourceforge.net/projects/tinyxml) - [zlib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/ZLIB.txt)
 * [zlib](http://www.gzip.org/zlib/) - [ZLIB license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/ZLIB.txt)
-* [modp_b64](https://github.com/aseprite/aseprite/tree/master/third_party/modp_b64/modp_b64.h) - [BSD license](https://github.com/aseprite/aseprite/tree/master/third_party/modp_b64/LICENSE)
 
 ## License
 
diff --git a/data/scripts/white_to_alpha.js b/data/scripts/white_to_alpha.js
index a469391..81eace4 100644
--- a/data/scripts/white_to_alpha.js
+++ b/data/scripts/white_to_alpha.js
@@ -1,15 +1,20 @@
 // Aseprite
-// Copyright (C) 2015 by David Capello
+// Copyright (C) 2015-2016 by David Capello
 
-var spr = activeSprite
+var col = app.pixelColor
+var img = app.activeSprite
 
-for (y=0; y<spr.height; ++y) {
-  for (x=0; x<spr.width; ++x) {
-    var c = spr.getPixel(x, y)
-    var v = (rgbaR(c)+rgbaG(c)+rgbaB(c))/3
-    spr.putPixel(x, y, rgba(rgbaR(c),
-                            rgbaG(c),
-                            rgbaB(c),
-                            255-v))
+for (y=0; y<img.height; ++y) {
+  for (x=0; x<img.width; ++x) {
+    var c = img.getPixel(x, y)
+    var v = (col.rgbaR(c)+
+             col.rgbaG(c)+
+             col.rgbaB(c))/3
+
+    img.putPixel(x, y,
+                 col.rgba(col.rgbaR(c),
+                          col.rgbaG(c),
+                          col.rgbaB(c),
+                          255-v))
   }
 }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bac9f3b..69467f1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -104,7 +104,7 @@ add_subdirectory(gfx)
 add_subdirectory(net)
 add_subdirectory(pen)
 add_subdirectory(render)
-add_subdirectory(scripting)
+add_subdirectory(script)
 add_subdirectory(she)
 add_subdirectory(ui)
 add_subdirectory(undo)
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index dde05da..7234cf9 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -318,7 +318,10 @@ add_library(app-lib
   res/palettes_loader_delegate.cpp
   res/resources_loader.cpp
   resource_finder.cpp
-  scripting/app_scripting.cpp
+  script/app_object.cpp
+  script/app_scripting.cpp
+  script/console_object.cpp
+  script/sprite_class.cpp
   send_crash.cpp
   shade.cpp
   shell.cpp
@@ -441,7 +444,7 @@ target_link_libraries(app-lib
   net-lib
   pen-lib
   render-lib
-  scripting-lib
+  script-lib
   she
   ui-lib
   undo-lib
diff --git a/src/app/app.cpp b/src/app/app.cpp
index f3dc7ff..ccde145 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -36,7 +36,7 @@
 #include "app/pref/preferences.h"
 #include "app/recent_files.h"
 #include "app/resource_finder.h"
-#include "app/scripting/app_scripting.h"
+#include "app/script/app_scripting.h"
 #include "app/send_crash.h"
 #include "app/shell.h"
 #include "app/tools/tool_box.h"
@@ -69,7 +69,7 @@
 #include "doc/sprite.h"
 #include "pen/pen.h"
 #include "render/render.h"
-#include "scripting/engine_delegate.h"
+#include "script/engine_delegate.h"
 #include "she/display.h"
 #include "she/error.h"
 #include "she/system.h"
@@ -138,7 +138,7 @@ public:
 
 };
 
-class StdoutEngineDelegate : public scripting::EngineDelegate {
+class StdoutEngineDelegate : public script::EngineDelegate {
 public:
   void onConsolePrint(const char* text) override {
     printf("%s\n", text);
diff --git a/src/app/commands/cmd_run_script.cpp b/src/app/commands/cmd_run_script.cpp
index 2e1b682..5524643 100644
--- a/src/app/commands/cmd_run_script.cpp
+++ b/src/app/commands/cmd_run_script.cpp
@@ -1,5 +1,5 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2001-2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
@@ -13,16 +13,16 @@
 #include "app/commands/params.h"
 #include "app/console.h"
 #include "app/resource_finder.h"
-#include "app/scripting/app_scripting.h"
+#include "app/script/app_scripting.h"
 #include "base/path.h"
-#include "scripting/engine_delegate.h"
+#include "script/engine_delegate.h"
 #include "ui/manager.h"
 
 #include <cstdio>
 
 namespace app {
 
-class ConsoleEngineDelegate : public scripting::EngineDelegate {
+class ConsoleEngineDelegate : public script::EngineDelegate {
 public:
   void onConsolePrint(const char* text) override {
     m_console.printf("%s\n", text);
diff --git a/src/app/script/app_object.cpp b/src/app/script/app_object.cpp
new file mode 100644
index 0000000..d0ce360
--- /dev/null
+++ b/src/app/script/app_object.cpp
@@ -0,0 +1,73 @@
+// Aseprite
+// Copyright (C) 2015-2016  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/script/console_object.h"
+
+#include "app/script/sprite_class.h"
+#include "app/ui_context.h"
+#include "script/engine.h"
+
+// App sub-objects
+#include "app/script/pixel_color.h"
+
+#include <iostream>
+
+namespace app {
+
+namespace {
+
+script::result_t App_get_activeSprite(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  app::Document* doc = UIContext::instance()->activeDocument();
+  if (doc)
+    ctx.pushObject(wrap_sprite(doc), "Sprite");
+  else
+    ctx.pushNull();
+  return 1;
+}
+
+script::result_t App_get_pixelColor(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushObject();
+  ctx.registerFuncs(-1, pixelColor_methods);
+  return 1;
+}
+
+script::result_t App_get_version(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushString(VERSION);
+  return 1;
+}
+
+const script::FunctionEntry App_methods[] = {
+  { nullptr, nullptr, 0 }
+};
+
+const script::PropertyEntry App_props[] = {
+  { "activeSprite", App_get_activeSprite, nullptr },
+  { "pixelColor", App_get_pixelColor, nullptr },
+  { "version", App_get_version, nullptr },
+  { nullptr, nullptr, 0 }
+};
+
+} // anonymous namespace
+
+void register_app_object(script::Context& ctx)
+{
+  ctx.pushGlobalObject();
+  ctx.registerObject(-1, "app", App_methods, App_props);
+  ctx.pop();
+}
+
+} // namespace app
diff --git a/src/app/shell.h b/src/app/script/app_object.h
similarity index 52%
copy from src/app/shell.h
copy to src/app/script/app_object.h
index 21e31ca..528ae43 100644
--- a/src/app/shell.h
+++ b/src/app/script/app_object.h
@@ -1,27 +1,21 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2015-2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
 // published by the Free Software Foundation.
 
-#ifndef APP_SHELL_H_INCLUDED
-#define APP_SHELL_H_INCLUDED
+#ifndef APP_SCRIPT_APP_OBJECT_H_INCLUDED
+#define APP_SCRIPT_APP_OBJECT_H_INCLUDED
 #pragma once
 
-namespace scripting {
-  class Engine;
+namespace script {
+  class Context;
 }
 
 namespace app {
 
-  class Shell {
-  public:
-    Shell();
-    ~Shell();
-
-    void run(scripting::Engine& engine);
-  };
+  void register_app_object(script::Context& ctx);
 
 } // namespace app
 
diff --git a/src/app/script/app_scripting.cpp b/src/app/script/app_scripting.cpp
new file mode 100644
index 0000000..db8c48b
--- /dev/null
+++ b/src/app/script/app_scripting.cpp
@@ -0,0 +1,32 @@
+// Aseprite
+// Copyright (C) 2001-2016  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/script/app_scripting.h"
+
+#include "app/script/app_object.h"
+#include "app/script/console_object.h"
+#include "app/script/sprite_class.h"
+
+namespace app {
+
+AppScripting::AppScripting(script::EngineDelegate* delegate)
+  : script::Engine(delegate)
+{
+  auto& ctx = context();
+  register_app_object(ctx);
+  register_console_object(ctx);
+
+  ctx.pushGlobalObject();
+  register_sprite_class(-1, ctx);
+  ctx.pop();
+}
+
+}
diff --git a/src/app/scripting/app_scripting.h b/src/app/script/app_scripting.h
similarity index 51%
rename from src/app/scripting/app_scripting.h
rename to src/app/script/app_scripting.h
index 37abf82..ac89a43 100644
--- a/src/app/scripting/app_scripting.h
+++ b/src/app/script/app_scripting.h
@@ -1,22 +1,21 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2001-2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
 // published by the Free Software Foundation.
 
-#ifndef APP_SCRIPTING_H_INCLUDED
-#define APP_SCRIPTING_H_INCLUDED
+#ifndef APP_SCRIPT_H_INCLUDED
+#define APP_SCRIPT_H_INCLUDED
 #pragma once
 
-#include "scripting/engine.h"
+#include "script/engine.h"
 
 namespace app {
-  class Document;
 
-  class AppScripting : public scripting::Engine {
+  class AppScripting : public script::Engine {
   public:
-    AppScripting(scripting::EngineDelegate* delegate);
+    AppScripting(script::EngineDelegate* delegate);
   };
 
 } // namespace app
diff --git a/src/app/script/console_object.cpp b/src/app/script/console_object.cpp
new file mode 100644
index 0000000..dc79792
--- /dev/null
+++ b/src/app/script/console_object.cpp
@@ -0,0 +1,65 @@
+// Aseprite
+// Copyright (C) 2001-2016  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/script/console_object.h"
+
+#include "app/app.h"
+#include "app/console.h"
+#include "script/engine.h"
+
+#include <iostream>
+
+namespace app {
+
+namespace {
+
+void print(const char* str)
+{
+  if (str) {
+    std::cout << str << std::endl; // New line + flush
+
+    if (App::instance()->isGui()) {
+      Console().printf("%s\n", str);
+    }
+  }
+}
+
+script::result_t Console_assert(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  if (!ctx.toBool(0))
+    print(ctx.toString(1));
+  return 0;
+}
+
+script::result_t Console_log(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  print(ctx.toString(0));
+  return 0;
+}
+
+const script::FunctionEntry Console_methods[] = {
+  { "assert", Console_assert, 2 },
+  { "log", Console_log, 1 },
+  { nullptr, nullptr, 0 }
+};
+
+} // anonymous namespace
+
+void register_console_object(script::Context& ctx)
+{
+  ctx.pushGlobalObject();
+  ctx.registerObject(-1, "console", Console_methods, nullptr);
+  ctx.pop();
+}
+
+} // namespace app
diff --git a/src/app/shell.h b/src/app/script/console_object.h
similarity index 52%
copy from src/app/shell.h
copy to src/app/script/console_object.h
index 21e31ca..bd62a56 100644
--- a/src/app/shell.h
+++ b/src/app/script/console_object.h
@@ -1,27 +1,21 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
 // published by the Free Software Foundation.
 
-#ifndef APP_SHELL_H_INCLUDED
-#define APP_SHELL_H_INCLUDED
+#ifndef APP_SCRIPT_CONSOLE_OBJECT_H_INCLUDED
+#define APP_SCRIPT_CONSOLE_OBJECT_H_INCLUDED
 #pragma once
 
-namespace scripting {
-  class Engine;
+namespace script {
+  class Context;
 }
 
 namespace app {
 
-  class Shell {
-  public:
-    Shell();
-    ~Shell();
-
-    void run(scripting::Engine& engine);
-  };
+  void register_console_object(script::Context& ctx);
 
 } // namespace app
 
diff --git a/src/app/script/pixel_color.h b/src/app/script/pixel_color.h
new file mode 100644
index 0000000..09e376d
--- /dev/null
+++ b/src/app/script/pixel_color.h
@@ -0,0 +1,86 @@
+// Aseprite
+// Copyright (C) 2016  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#include "doc/color.h"
+
+namespace {
+
+script::result_t pixelColor_rgba(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int r = ctx.requireInt(0);
+  int g = ctx.requireInt(1);
+  int b = ctx.requireInt(2);
+  int a = (ctx.isUndefined(3) ? 255: ctx.requireInt(3));
+  ctx.pushUInt(doc::rgba(r, g, b, a));
+  return 1;
+}
+
+script::result_t pixelColor_rgbaR(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushUInt(doc::rgba_getr(ctx.requireUInt(0)));
+  return 1;
+}
+
+script::result_t pixelColor_rgbaG(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushUInt(doc::rgba_getg(ctx.requireUInt(0)));
+  return 1;
+}
+
+script::result_t pixelColor_rgbaB(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushUInt(doc::rgba_getb(ctx.requireUInt(0)));
+  return 1;
+}
+
+script::result_t pixelColor_rgbaA(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushUInt(doc::rgba_geta(ctx.requireUInt(0)));
+  return 1;
+}
+
+script::result_t pixelColor_graya(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int v = ctx.requireInt(0);
+  int a = (ctx.isUndefined(1) ? 255: ctx.requireInt(1));
+  ctx.pushUInt(doc::graya(v, a));
+  return 1;
+}
+
+script::result_t pixelColor_grayaV(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushUInt(doc::graya_getv(ctx.requireUInt(0)));
+  return 1;
+}
+
+script::result_t pixelColor_grayaA(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  ctx.pushUInt(doc::graya_geta(ctx.requireUInt(0)));
+  return 1;
+}
+
+const script::FunctionEntry pixelColor_methods[] = {
+  { "rgba", pixelColor_rgba, 4 },
+  { "rgbaR", pixelColor_rgbaR, 1 },
+  { "rgbaG", pixelColor_rgbaG, 1 },
+  { "rgbaB", pixelColor_rgbaB, 1 },
+  { "rgbaA", pixelColor_rgbaA, 1 },
+  { "graya", pixelColor_graya, 2 },
+  { "grayaV", pixelColor_grayaV, 1 },
+  { "grayaA", pixelColor_grayaA, 1 },
+  { nullptr, nullptr, 0 }
+};
+
+} // anonymous namespace
diff --git a/src/app/script/sprite_class.cpp b/src/app/script/sprite_class.cpp
new file mode 100644
index 0000000..8395861
--- /dev/null
+++ b/src/app/script/sprite_class.cpp
@@ -0,0 +1,227 @@
+// Aseprite
+// Copyright (C) 2015-2016  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/script/sprite_class.h"
+
+#include "app/document.h"
+#include "app/document_api.h"
+#include "app/transaction.h"
+#include "app/ui/document_view.h"
+#include "app/ui_context.h"
+#include "doc/site.h"
+#include "doc/sprite.h"
+#include "script/engine.h"
+
+#include <map>
+#include <iostream>
+
+namespace app {
+
+namespace {
+
+class SpriteInScript {
+public:
+  SpriteInScript(app::Document* doc)
+    : m_doc(doc) {
+  }
+
+  ~SpriteInScript() {
+  }
+
+  app::Document* document() {
+    return m_doc;
+  }
+
+private:
+  app::Document* m_doc;
+};
+
+script::result_t Sprite_ctor(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  if (ctx.isConstructorCall()) {
+    int w = ctx.requireInt(0);
+    int h = ctx.requireInt(1);
+    int colorMode = (ctx.isUndefined(2) ? IMAGE_RGB: ctx.requireInt(2));
+
+    base::UniquePtr<Sprite> sprite(
+      Sprite::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256));
+    base::UniquePtr<Document> doc(new Document(sprite));
+    sprite.release();
+
+    doc->setContext(UIContext::instance());
+    ctx.pushThis(wrap_sprite(doc.release()));
+  }
+  return 0;
+}
+
+script::result_t Sprite_putPixel(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int x = ctx.requireInt(0);
+  int y = ctx.requireInt(1);
+  doc::color_t color = ctx.requireUInt(2);
+
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  DocumentView* docView = UIContext::instance()->getFirstDocumentView(doc);
+  if (!docView)
+    return 0;
+
+  doc::Site site;
+  docView->getSite(&site);
+
+  int celX, celY;
+  doc::Image* image = site.image(&celX, &celY, nullptr);
+  if (image)
+    image->putPixel(x-celX, y-celY, color);
+
+  return 0;
+}
+
+script::result_t Sprite_getPixel(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int x = ctx.requireInt(0);
+  int y = ctx.requireInt(1);
+
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  DocumentView* docView = UIContext::instance()->getFirstDocumentView(doc);
+  if (!docView)
+    return 0;
+
+  doc::Site site;
+  docView->getSite(&site);
+
+  int celX, celY;
+  doc::Image* image = site.image(&celX, &celY, nullptr);
+  if (image) {
+    doc::color_t color = image->getPixel(x-celX, y-celY);
+    ctx.pushUInt(color);
+    return 1;
+  }
+  else
+    return 0;
+}
+
+script::result_t Sprite_resize(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int w = ctx.requireInt(0);
+  int h = ctx.requireInt(1);
+
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  {
+    Transaction transaction(UIContext::instance(), "Script Execution", ModifyDocument);
+    DocumentApi api(doc, transaction);
+    api.setSpriteSize(doc->sprite(), w, h);
+    transaction.commit();
+  }
+
+  return 0;
+}
+
+script::result_t Sprite_crop(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int x = ctx.requireInt(0);
+  int y = ctx.requireInt(1);
+  int w = ctx.requireInt(2);
+  int h = ctx.requireInt(3);
+
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  {
+    Transaction transaction(UIContext::instance(), "Script Execution", ModifyDocument);
+    DocumentApi api(doc, transaction);
+    api.cropSprite(doc->sprite(), gfx::Rect(x, y, w, h));
+    transaction.commit();
+  }
+
+  return 0;
+}
+
+script::result_t Sprite_get_width(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  ctx.pushInt(doc->sprite()->width());
+  return 1;
+}
+
+script::result_t Sprite_set_width(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int w = ctx.requireInt(0);
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  doc->sprite()->setSize(w, doc->sprite()->height());
+  return 0;
+}
+
+script::result_t Sprite_get_height(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  ctx.pushInt(doc->sprite()->height());
+  return 1;
+}
+
+script::result_t Sprite_set_height(script::ContextHandle handle)
+{
+  script::Context ctx(handle);
+  int h = ctx.requireInt(0);
+  Document* doc = (Document*)unwrap_sprite(ctx.getThis());
+  doc->sprite()->setSize(doc->sprite()->width(), h);
+  return 0;
+}
+
+const script::FunctionEntry Sprite_methods[] = {
+  { "getPixel", Sprite_getPixel, 2 },
+  { "putPixel", Sprite_putPixel, 3 },
+  { "resize", Sprite_resize, 2 },
+  { "crop", Sprite_crop, 4 },
+  { nullptr, nullptr, 0 }
+};
+
+const script::PropertyEntry Sprite_props[] = {
+  { "width", Sprite_get_width, Sprite_set_width },
+  { "height", Sprite_get_height, Sprite_set_height },
+  { nullptr, nullptr, 0 }
+};
+
+} // anonymous namespace
+
+static std::map<doc::ObjectId, SpriteInScript*> g_sprites;
+
+void* wrap_sprite(app::Document* doc)
+{
+  auto it = g_sprites.find(doc->id());
+  if (it != g_sprites.end())
+    return it->second;
+  else {
+    SpriteInScript* wrap = new SpriteInScript(doc);
+    g_sprites[doc->id()] = wrap;
+    return wrap;
+  }
+}
+
+app::Document* unwrap_sprite(void* ptr)
+{
+  if (ptr)
+    return ((SpriteInScript*)ptr)->document();
+  else
+    return nullptr;
+}
+
+void register_sprite_class(script::index_t idx, script::Context& ctx)
+{
+  ctx.registerClass(idx, "Sprite", Sprite_ctor, 3, Sprite_methods, Sprite_props);
+}
+
+} // namespace app
diff --git a/src/app/script/sprite_class.h b/src/app/script/sprite_class.h
new file mode 100644
index 0000000..98ab6e2
--- /dev/null
+++ b/src/app/script/sprite_class.h
@@ -0,0 +1,24 @@
+// Aseprite
+// Copyright (C) 2015-2016  David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifndef APP_SCRIPT_SPRITE_CLASS_H_INCLUDED
+#define APP_SCRIPT_SPRITE_CLASS_H_INCLUDED
+#pragma once
+
+#include "script/engine.h"
+
+namespace app {
+  class Document;
+
+  void* wrap_sprite(app::Document* doc);
+  app::Document* unwrap_sprite(void* ptr);
+
+  void register_sprite_class(script::index_t idx, script::Context& ctx);
+
+} // namespace app
+
+#endif
diff --git a/src/app/scripting/app_scripting.cpp b/src/app/scripting/app_scripting.cpp
deleted file mode 100644
index 386ab50..0000000
--- a/src/app/scripting/app_scripting.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-// Aseprite
-// Copyright (C) 2001-2015  David Capello
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation.
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "app/scripting/app_scripting.h"
-
-#include "app/document.h"
-#include "app/document_api.h"
-#include "app/transaction.h"
-#include "app/ui/document_view.h"
-#include "app/ui_context.h"
-#include "doc/color.h"
-#include "doc/image.h"
-#include "doc/site.h"
-
-namespace app {
-
-#include "app/scripting/raw_color.h"
-#include "app/scripting/sprite.h"
-
-AppScripting::AppScripting(scripting::EngineDelegate* delegate)
-  : scripting::Engine(delegate)
-{
-  registerFunction("rgba", rgba, 4);
-  registerFunction("rgbaR", rgbaR, 1);
-  registerFunction("rgbaG", rgbaG, 1);
-  registerFunction("rgbaB", rgbaB, 1);
-  registerFunction("rgbaA", rgbaA, 1);
-  registerClass("Sprite", Sprite_ctor, 3, Sprite_methods, Sprite_props);
-  registerGlobal("activeSprite", activeSprite_getter, activeSprite_setter);
-}
-
-}
diff --git a/src/app/scripting/raw_color.h b/src/app/scripting/raw_color.h
deleted file mode 100644
index 1bfbb1c..0000000
--- a/src/app/scripting/raw_color.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Aseprite
-// Copyright (C) 2001-2015  David Capello
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation.
-
-namespace {
-
-scripting::result_t rgba(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  int r = ctx.requireInt(0);
-  int g = ctx.requireInt(1);
-  int b = ctx.requireInt(2);
-  int a = (ctx.isUndefined(3) ? 255: ctx.requireInt(3));
-  ctx.pushUInt(doc::rgba(r, g, b, a));
-  return 1;
-}
-
-scripting::result_t rgbaR(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  ctx.pushUInt(doc::rgba_getr(ctx.requireUInt(0)));
-  return 1;
-}
-
-scripting::result_t rgbaG(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  ctx.pushUInt(doc::rgba_getg(ctx.requireUInt(0)));
-  return 1;
-}
-
-scripting::result_t rgbaB(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  ctx.pushUInt(doc::rgba_getb(ctx.requireUInt(0)));
-  return 1;
-}
-
-scripting::result_t rgbaA(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  ctx.pushUInt(doc::rgba_geta(ctx.requireUInt(0)));
-  return 1;
-}
-
-} // anonymous namespace
diff --git a/src/app/scripting/sprite.h b/src/app/scripting/sprite.h
deleted file mode 100644
index dceafa0..0000000
--- a/src/app/scripting/sprite.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// Aseprite
-// Copyright (C) 2001-2015  David Capello
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation.
-
-namespace {
-
-scripting::result_t Sprite_ctor(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  if (ctx.isConstructorCall()) {
-    int w = ctx.requireInt(0);
-    int h = ctx.requireInt(1);
-    int colorMode = (ctx.isUndefined(2) ? IMAGE_RGB: ctx.requireInt(2));
-
-    base::UniquePtr<Sprite> sprite(
-      Sprite::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256));
-    base::UniquePtr<Document> doc(new Document(sprite));
-    sprite.release();
-
-    doc->setContext(UIContext::instance());
-    ctx.pushThis(doc.release());
-  }
-  return 0;
-}
-
-scripting::result_t Sprite_putPixel(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  int x = ctx.requireInt(0);
-  int y = ctx.requireInt(1);
-  doc::color_t color = ctx.requireUInt(2);
-
-  Document* doc = (Document*)ctx.getThis();
-  DocumentView* docView = UIContext::instance()->getFirstDocumentView(doc);
-  if (!docView)
-    return 0;
-
-  doc::Site site;
-  docView->getSite(&site);
-
-  int celX, celY;
-  doc::Image* image = site.image(&celX, &celY, nullptr);
-  if (image)
-    image->putPixel(x-celX, y-celY, color);
-
-  return 0;
-}
-
-scripting::result_t Sprite_getPixel(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  int x = ctx.requireInt(0);
-  int y = ctx.requireInt(1);
-
-  Document* doc = (Document*)ctx.getThis();
-  DocumentView* docView = UIContext::instance()->getFirstDocumentView(doc);
-  if (!docView)
-    return 0;
-
-  doc::Site site;
-  docView->getSite(&site);
-
-  int celX, celY;
-  doc::Image* image = site.image(&celX, &celY, nullptr);
-  if (image) {
-    doc::color_t color = image->getPixel(x-celX, y-celY);
-    ctx.pushUInt(color);
-    return 1;
-  }
-  else
-    return 0;
-}
-
-scripting::result_t Sprite_resize(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  int w = ctx.requireInt(0);
-  int h = ctx.requireInt(1);
-
-  Document* doc = (Document*)ctx.getThis();
-  {
-    Transaction transaction(UIContext::instance(), "Script Execution", ModifyDocument);
-    DocumentApi api(doc, transaction);
-    api.setSpriteSize(doc->sprite(), w, h);
-    transaction.commit();
-  }
-
-  return 0;
-}
-
-scripting::result_t Sprite_get_width(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  Document* doc = (Document*)ctx.getThis();
-  ctx.pushInt(doc->sprite()->width());
-  return 1;
-}
-
-scripting::result_t Sprite_get_height(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  Document* doc = (Document*)ctx.getThis();
-  ctx.pushInt(doc->sprite()->height());
-  return 1;
-}
-
-scripting::result_t activeSprite_getter(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  app::Document* doc = UIContext::instance()->activeDocument();
-  if (doc)
-    ctx.pushObject(doc, "Sprite");
-  else
-    ctx.pushNull();
-  return 1;
-}
-
-scripting::result_t activeSprite_setter(scripting::ContextHandle handle)
-{
-  scripting::Context ctx(handle);
-  Document* doc = (Document*)ctx.requireObject(0, "Sprite");
-  if (doc)
-    UIContext::instance()->setActiveDocument(doc);
-  return 0;
-}
-
-const scripting::FunctionEntry Sprite_methods[] = {
-  { "putPixel", Sprite_putPixel, 3 },
-  { "getPixel", Sprite_getPixel, 2 },
-  { "resize", Sprite_resize, 2 },
-  { nullptr, nullptr, 0 }
-};
-
-const scripting::PropertyEntry Sprite_props[] = {
-  { "width", Sprite_get_width, nullptr },
-  { "height", Sprite_get_height, nullptr },
-  { nullptr, nullptr, 0 }
-};
-
-} // anonymous namespace
diff --git a/src/app/shell.cpp b/src/app/shell.cpp
index 1bf14e6..1225410 100644
--- a/src/app/shell.cpp
+++ b/src/app/shell.cpp
@@ -1,5 +1,5 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2001-2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
@@ -11,7 +11,7 @@
 
 #include "app/shell.h"
 
-#include "scripting/engine.h"
+#include "script/engine.h"
 
 #include <iostream>
 #include <string>
@@ -26,7 +26,7 @@ Shell::~Shell()
 {
 }
 
-void Shell::run(scripting::Engine& engine)
+void Shell::run(script::Engine& engine)
 {
   std::cout << "Welcome to " PACKAGE " v" VERSION " interactive console" << std::endl;
   std::string line;
diff --git a/src/app/shell.h b/src/app/shell.h
index 21e31ca..cb7f3d7 100644
--- a/src/app/shell.h
+++ b/src/app/shell.h
@@ -1,5 +1,5 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2001-2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
@@ -9,7 +9,7 @@
 #define APP_SHELL_H_INCLUDED
 #pragma once
 
-namespace scripting {
+namespace script {
   class Engine;
 }
 
@@ -20,7 +20,7 @@ namespace app {
     Shell();
     ~Shell();
 
-    void run(scripting::Engine& engine);
+    void run(script::Engine& engine);
   };
 
 } // namespace app
diff --git a/src/app/ui/devconsole_view.h b/src/app/ui/devconsole_view.h
index 9bcd0b0..3fd60de 100644
--- a/src/app/ui/devconsole_view.h
+++ b/src/app/ui/devconsole_view.h
@@ -1,5 +1,5 @@
 // Aseprite
-// Copyright (C) 2001-2015  David Capello
+// Copyright (C) 2001-2016  David Capello
 //
 // This program is free software; you can redistribute it and/or modify
 // it under the terms of the GNU General Public License version 2 as
@@ -9,10 +9,10 @@
 #define APP_UI_DEVCONSOLE_VIEW_H_INCLUDED
 #pragma once
 
-#include "app/scripting/app_scripting.h"
+#include "app/script/app_scripting.h"
 #include "app/ui/tabs.h"
 #include "app/ui/workspace_view.h"
-#include "scripting/engine_delegate.h"
+#include "script/engine_delegate.h"
 #include "ui/box.h"
 #include "ui/label.h"
 #include "ui/textbox.h"
@@ -22,7 +22,7 @@ namespace app {
   class DevConsoleView : public ui::Box
                        , public TabView
                        , public WorkspaceView
-                       , public scripting::EngineDelegate {
+                       , public script::EngineDelegate {
   public:
     DevConsoleView();
     ~DevConsoleView();
diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt
new file mode 100644
index 0000000..783d7d6
--- /dev/null
+++ b/src/script/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Aseprite Scripting Library
+# Copyright (C) 2015-2016  David Capello
+
+include_directories(${DUKTAPE_DIR})
+
+add_library(duktape ${DUKTAPE_DIR}/duktape.c)
+add_library(script-lib engine.cpp)
+
+target_link_libraries(script-lib duktape)
diff --git a/src/scripting/LICENSE.txt b/src/script/LICENSE.txt
similarity index 96%
rename from src/scripting/LICENSE.txt
rename to src/script/LICENSE.txt
index 11f1e5e..24fd483 100644
--- a/src/scripting/LICENSE.txt
+++ b/src/script/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2015 David Capello
+Copyright (c) 2015-2016 David Capello
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
diff --git a/src/scripting/README.md b/src/script/README.md
similarity index 65%
rename from src/scripting/README.md
rename to src/script/README.md
index 3862e85..2bf61ab 100644
--- a/src/scripting/README.md
+++ b/src/script/README.md
@@ -1,4 +1,4 @@
 # Aseprite Scripting Library
-*Copyright (C) 2015 David Capello*
+*Copyright (C) 2015-2016 David Capello*
 
 > Distributed under [MIT license](LICENSE.txt)
diff --git a/src/script/engine.cpp b/src/script/engine.cpp
new file mode 100644
index 0000000..2eae72a
--- /dev/null
+++ b/src/script/engine.cpp
@@ -0,0 +1,507 @@
+// Aseprite Scripting Library
+// Copyright (c) 2015-2016 David Capello
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "script/engine.h"
+
+#include "base/convert_to.h"
+#include "base/exception.h"
+#include "base/file_handle.h"
+#include "base/memory.h"
+#include "script/engine_delegate.h"
+
+#include <map>
+#include <iostream>
+
+#include <duktape.h>
+
+class ScriptEngineException : public base::Exception {
+public:
+  ScriptEngineException(duk_errcode_t code, const char* msg)
+    : Exception(std::string(msg) + " (code " + base::convert_to<std::string>(code) + ")") {
+  }
+};
+
+namespace script {
+
+namespace {
+
+// TODO classes in modules isn't supported yet
+std::map<std::string, Module*> g_modules;
+
+void* on_alloc_function(void* udata, duk_size_t size)
+{
+  if (size)
+    return base_malloc(size);
+  else
+    return nullptr;
+}
+
+void* on_realloc_function(void* udata, void* ptr, duk_size_t size)
+{
+  if (!ptr) {
+    if (size)
+      return base_malloc(size);
+    else
+      return nullptr;
+  }
+  else if (!size) {
+    base_free(ptr);
+    return nullptr;
+  }
+  else
+    return base_realloc(ptr, size);
+}
+
+void on_free_function(void* udata, void* ptr)
+{
+  if (ptr)
+    base_free(ptr);
+}
+
+duk_ret_t on_search_module(duk_context* ctx)
+{
+  const char* id = duk_get_string(ctx, 0);
+  if (!id)
+    return 0;
+
+  auto it = g_modules.find(id);
+  if (it == g_modules.end()) {
+    // TODO error module not found
+    return 0;
+  }
+
+  Module* module = it->second;
+  Context ctxWrapper(ctx);
+  if (module->registerModule(ctxWrapper) > 0) {
+    // Overwrite the 'exports' property of the module (arg 3)
+    // with the object returned by registerModule()
+    duk_put_prop_string(ctx, 3, "exports");
+  }
+
+  // 0 means no source code
+  return 0;
+}
+
+void on_fatal_handler(duk_context* ctx, duk_errcode_t code, const char* msg)
+{
+  throw ScriptEngineException(code, msg);
+}
+
+}
+
+void Context::dump()
+{
+  duk_push_context_dump(m_handle);
+  std::cout << duk_to_string(m_handle, -1) << std::endl;
+  duk_pop(m_handle);
+}
+
+void Context::pop()
+{
+  duk_pop(m_handle);
+}
+
+void Context::remove(index_t idx)
+{
+  duk_remove(m_handle, idx);
+}
+
+void Context::duplicateTop()
+{
+  duk_dup_top(m_handle);
+}
+
+bool Context::isConstructorCall()
+{
+  return (duk_is_constructor_call(m_handle) ? true: false);
+}
+
+bool Context::isUndefined(index_t i)
+{
+  return (duk_is_undefined(m_handle, i) ? true: false);
+}
+
+bool Context::isNull(index_t i)
+{
+  return (duk_is_null(m_handle, i) ? true: false);
+}
+
+bool Context::isNullOrUndefined(index_t i)
+{
+  return (duk_is_null_or_undefined(m_handle, i) ? true: false);
+}
+
+bool Context::isBool(index_t i)
+{
+  return (duk_is_boolean(m_handle, i) ? true: false);
+}
+
+bool Context::isNumber(index_t i)
+{
+  return (duk_is_number(m_handle, i) ? true: false);
+}
+
+bool Context::isNaN(index_t i)
+{
+  return (duk_is_nan(m_handle, i) ? true: false);
+}
+
+bool Context::isString(index_t i)
+{
+  return (duk_is_string(m_handle, i) ? true: false);
+}
+
+bool Context::getBool(index_t i)
+{
+  return (duk_get_boolean(m_handle, i) ? true: false);
+}
+
+bool Context::toBool(index_t i)
+{
+  return (duk_to_boolean(m_handle, i) ? true: false);
+}
+
+double Context::getNumber(index_t i)
+{
+  return duk_get_number(m_handle, i);
+}
+
+int Context::getInt(index_t i)
+{
+  return duk_get_int(m_handle, i);
+}
+
+unsigned int Context::getUInt(index_t i)
+{
+  return duk_get_uint(m_handle, i);
+}
+
+const char* Context::getString(index_t i)
+{
+  return duk_get_string(m_handle, i);
+}
+
+const char* Context::toString(index_t i)
+{
+  return duk_to_string(m_handle, i);
+}
+
+bool Context::requireBool(index_t i)
+{
+  return (duk_require_boolean(m_handle, i) ? true: false);
+}
+
+double Context::requireNumber(index_t i)
+{
+  return duk_require_number(m_handle, i);
+}
+
+int Context::requireInt(index_t i)
+{
+  return duk_require_int(m_handle, i);
+}
+
+unsigned int Context::requireUInt(index_t i)
+{
+  return duk_require_uint(m_handle, i);
+}
+
+const char* Context::requireString(index_t i)
+{
+  return duk_require_string(m_handle, i);
+}
+
+void* Context::requireObject(index_t i, const char* className)
+{
+  duk_get_prop_string(m_handle, i, "\xFF" "\xFF" "ptr");
+  void* result = (void*)duk_to_pointer(m_handle, -1);
+  // TODO check pointer type
+  duk_pop(m_handle);
+  return result;
+}
+
+void Context::pushUndefined()
+{
+  duk_push_undefined(m_handle);
+}
+
+void Context::pushNull()
+{
+  duk_push_null(m_handle);
+}
+
+void Context::pushBool(bool val)
+{
+  duk_push_boolean(m_handle, val);
+}
+
+void Context::pushNumber(double val)
+{
+  duk_push_number(m_handle, val);
+}
+
+void Context::pushNaN()
+{
+  duk_push_nan(m_handle);
+}
+
+void Context::pushInt(int val)
+{
+  duk_push_int(m_handle, val);
+}
+
+void Context::pushUInt(unsigned int val)
+{
+  duk_push_uint(m_handle, val);
+}
+
+void Context::pushString(const char* str)
+{
+  duk_push_string(m_handle, str);
+}
+
+void Context::pushThis()
+{
+  duk_push_this(m_handle);
+}
+
+void Context::pushThis(void* ptr)
+{
+  duk_push_this(m_handle);
+  duk_push_pointer(m_handle, ptr);
+  duk_put_prop_string(m_handle, -2, "\xFF" "\xFF" "ptr");
+}
+
+index_t Context::pushObject()
+{
+  return duk_push_object(m_handle);
+}
+
+index_t Context::pushObject(void* ptr, const char* className)
+{
+  index_t obj = duk_push_object(m_handle);
+  duk_push_pointer(m_handle, ptr);
+  duk_put_prop_string(m_handle, obj, "\xFF" "\xFF" "ptr");
+
+  // TODO classes in modules isn't supported yet
+  duk_get_global_string(m_handle, className);
+  duk_get_prototype(m_handle, -1);
+  duk_set_prototype(m_handle, obj);
+  duk_pop(m_handle);
+
+  return obj;
+}
+
+void Context::pushGlobalObject()
+{
+  duk_push_global_object(m_handle);
+}
+
+void Context::registerProp(index_t idx,
+                           const char* id,
+                           Function getter,
+                           Function setter)
+{
+  duk_push_string(m_handle, id);
+  if (idx < 0)
+    --idx;
+
+  duk_uint_t flags = 0;
+
+  if (getter) {
+    flags |= DUK_DEFPROP_HAVE_GETTER;
+    duk_push_c_function(m_handle, getter, 0);
+    if (idx < 0)
+      --idx;
+  }
+
+  if (setter) {
+    flags |= DUK_DEFPROP_HAVE_SETTER;
+    duk_push_c_function(m_handle, setter, 1);
+    if (idx < 0)
+      --idx;
+  }
+
+  duk_def_prop(m_handle, idx, flags);
+}
+
+void Context::registerProps(index_t idx, const PropertyEntry* props)
+{
+  for (int i=0; props[i].id; ++i) {
+    registerProp(idx,
+                 props[i].id,
+                 props[i].getter,
+                 props[i].setter);
+  }
+}
+
+void Context::registerFunc(index_t idx,
+                           const char* id,
+                           const Function func,
+                           index_t nargs)
+{
+  duk_push_c_function(m_handle, func, nargs);
+  duk_put_prop_string(m_handle, idx, id);
+}
+
+void Context::registerFuncs(index_t idx, const FunctionEntry* methods)
+{
+  duk_put_function_list(m_handle, idx, (const duk_function_list_entry*)methods);
+}
+
+void Context::registerObject(index_t idx,
+                             const char* id,
+                             const FunctionEntry* methods,
+                             const PropertyEntry* props)
+{
+  duk_push_object(m_handle);
+  if (idx < 0)
+    --idx;
+
+  if (methods)
+    registerFuncs(-1, methods);
+  if (props)
+    registerProps(-1, props);
+
+  duk_put_prop_string(m_handle, idx, id);
+}
+
+void Context::registerClass(index_t idx,
+                            const char* id,
+                            Function ctorFunc, int ctorNargs,
+                            const FunctionEntry* methods,
+                            const PropertyEntry* props)
+{
+  ASSERT(ctorFunc);
+  duk_push_c_function(m_handle, ctorFunc, ctorNargs);
+
+  duk_push_object(m_handle); // Prototype object
+ if (methods)
+    duk_put_function_list(m_handle, -1, (const duk_function_list_entry*)methods);
+  if (props)
+    registerProps(-1, props);
+
+  duk_set_prototype(m_handle, -2);
+  duk_put_prop_string(m_handle, idx-1, id);
+}
+
+void* Context::getThis()
+{
+  duk_push_this(m_handle);
+  duk_get_prop_string(m_handle, -1, "\xFF" "\xFF" "ptr");
+  void* result = (void*)duk_to_pointer(m_handle, -1);
+  duk_pop(m_handle);
+  return result;
+}
+
+bool Context::hasProp(index_t i, const char* propName)
+{
+  return (duk_has_prop_string(m_handle, i, propName) ? true: false);
+}
+
+void Context::getProp(index_t i, const char* propName)
+{
+  duk_get_prop_string(m_handle, i, propName);
+}
+
+void Context::setProp(index_t i, const char* propName)
+{
+  duk_put_prop_string(m_handle, i, propName);
+}
+
+Engine::Engine(EngineDelegate* delegate)
+  : m_ctx(duk_create_heap(&on_alloc_function,
+                          &on_realloc_function,
+                          &on_free_function,
+                          (void*)this,
+                          &on_fatal_handler))
+  , m_delegate(delegate)
+{
+  // Set 'on_search_module' as the function to search modules with
+  // require('modulename') on JavaScript.
+  duk_get_global_string(m_ctx.handle(), "Duktape");
+  duk_push_c_function(m_ctx.handle(), &on_search_module, 4);
+  duk_put_prop_string(m_ctx.handle(), -2, "modSearch");
+  duk_pop(m_ctx.handle());
+}
+
+Engine::~Engine()
+{
+  duk_destroy_heap(m_ctx.handle());
+}
+
+void Engine::eval(const std::string& jsCode)
+{
+  try {
+    ContextHandle handle = m_ctx.handle();
+
+    duk_eval_string(handle, jsCode.c_str());
+
+    if (!duk_is_null_or_undefined(handle, -1))
+      m_delegate->onConsolePrint(duk_safe_to_string(handle, -1));
+
+    duk_pop(handle);
+  }
+  catch (const std::exception& ex) {
+    std::string err = "Error: ";
+    err += ex.what();
+    m_delegate->onConsolePrint(err.c_str());
+  }
+}
+
+void Engine::evalFile(const std::string& file)
+{
+  try {
+    ContextHandle handle = m_ctx.handle();
+
+    base::FileHandle fhandle(base::open_file(file, "rb"));
+    FILE* f = fhandle.get();
+    if (!f)
+      return;
+
+    if (fseek(f, 0, SEEK_END) < 0)
+      return;
+
+    int sz = ftell(f);
+    if (sz < 0)
+      return;
+
+    if (fseek(f, 0, SEEK_SET) < 0)
+      return;
+
+    char* buf = (char*)duk_push_fixed_buffer(handle, sz);
+    ASSERT(buf != nullptr);
+    if (fread(buf, 1, sz, f) != sz)
+      return;
+
+    fclose(f);
+    f = nullptr;
+
+    duk_push_string(handle, duk_to_string(handle, -1));
+    duk_eval_raw(handle, nullptr, 0, DUK_COMPILE_EVAL);
+
+    if (!duk_is_null_or_undefined(handle, -1))
+      m_delegate->onConsolePrint(duk_safe_to_string(handle, -1));
+
+    duk_pop(handle);
+  }
+  catch (const std::exception& ex) {
+    std::string err = "Error: ";
+    err += ex.what();
+    m_delegate->onConsolePrint(err.c_str());
+  }
+}
+
+void Engine::registerModule(Module* module)
+{
+  g_modules[module->id()] = module;
+}
+
+} // namespace script
diff --git a/src/scripting/engine.h b/src/script/engine.h
similarity index 54%
rename from src/scripting/engine.h
rename to src/script/engine.h
index 992eb7e..c1ff9de 100644
--- a/src/scripting/engine.h
+++ b/src/script/engine.h
@@ -1,22 +1,44 @@
 // Aseprite Scripting Library
-// Copyright (c) 2015 David Capello
+// Copyright (c) 2015-2016 David Capello
 //
 // This file is released under the terms of the MIT license.
 // Read LICENSE.txt for more information.
 
-#ifndef SCRIPTING_ENGINE_H_INCLUDED
-#define SCRIPTING_ENGINE_H_INCLUDED
+#ifndef SCRIPT_ENGINE_H_INCLUDED
+#define SCRIPT_ENGINE_H_INCLUDED
 #pragma once
 
 #include <string>
 
-namespace scripting {
+struct duk_hthread;
+
+namespace script {
+  class Context;
   class EngineDelegate;
 
   typedef int result_t;
   typedef int index_t;
+  typedef struct duk_hthread* ContextHandle;
+  typedef result_t (*Function)(ContextHandle ctx);
+
+  struct FunctionEntry {
+    const char* id;
+    Function value;
+    index_t nargs;
+  };
+
+  struct PropertyEntry {
+    const char* id;
+    Function getter;
+    Function setter;
+  };
 
-  typedef void* ContextHandle;
+  class Module {
+  public:
+    virtual ~Module() { }
+    virtual const char* id() const = 0;
+    virtual int registerModule(Context& ctx) = 0;
+  };
 
   class Context {
   public:
@@ -24,6 +46,11 @@ namespace scripting {
 
     ContextHandle handle() { return m_handle; }
 
+    void dump();
+    void pop();
+    void remove(index_t idx);
+    void duplicateTop();
+
     bool isConstructorCall();
 
     bool isUndefined(index_t i);
@@ -35,12 +62,18 @@ namespace scripting {
     bool isString(index_t i);
 
     bool getBool(index_t i);
+    bool toBool(index_t i);
     double getNumber(index_t i);
     int getInt(index_t i);
     unsigned int getUInt(index_t i);
     const char* getString(index_t i);
+    const char* toString(index_t i);
     void* getThis();
 
+    bool hasProp(index_t i, const char* propName);
+    void getProp(index_t i, const char* propName);
+    void setProp(index_t i, const char* propName);
+
     bool requireBool(index_t i);
     double requireNumber(index_t i);
     int requireInt(index_t i);
@@ -56,27 +89,38 @@ namespace scripting {
     void pushInt(int val);
     void pushUInt(unsigned int val);
     void pushString(const char* str);
+    void pushThis();
     void pushThis(void* ptr);
-    void pushObject(void* ptr, const char* className);
+    index_t pushObject();
+    index_t pushObject(void* ptr, const char* className);
+    void pushGlobalObject();
+
+    void registerProp(index_t idx,
+                      const char* id,
+                      Function getter,
+                      Function setter);
+    void registerProps(index_t idx,
+                       const PropertyEntry* props);
+    void registerFunc(index_t idx,
+                      const char* id,
+                      const Function func,
+                      index_t nargs);
+    void registerFuncs(index_t idx,
+                       const FunctionEntry* methods);
+    void registerObject(index_t idx,
+                        const char* id,
+                        const FunctionEntry* methods,
+                        const PropertyEntry* props);
+    void registerClass(index_t idx,
+                       const char* id,
+                       Function ctorFunc, int ctorNargs,
+                       const FunctionEntry* methods,
+                       const PropertyEntry* props);
 
   private:
     ContextHandle m_handle;
   };
 
-  typedef result_t (*Function)(ContextHandle ctx);
-
-  struct FunctionEntry {
-    const char* key;
-    Function value;
-    index_t nargs;
-  };
-
-  struct PropertyEntry {
-    const char* key;
-    Function getter;
-    Function setter;
-  };
-
   class Engine {
   public:
     Engine(EngineDelegate* delegate);
@@ -85,13 +129,9 @@ namespace scripting {
     void eval(const std::string& jsCode);
     void evalFile(const std::string& file);
 
-    void registerFunction(const char* id, Function function, int nargs);
-    void registerClass(const char* id,
-                       Function ctorFunc, int ctorNargs,
-                       const FunctionEntry* methods,
-                       const PropertyEntry* props);
-    void registerGlobal(const char* id,
-                        Function getter, Function setter);
+    Context& context() { return m_ctx; }
+
+    void registerModule(Module* module);
 
   private:
     Context m_ctx;
diff --git a/src/scripting/engine_delegate.h b/src/script/engine_delegate.h
similarity index 66%
rename from src/scripting/engine_delegate.h
rename to src/script/engine_delegate.h
index b4e64b7..b835141 100644
--- a/src/scripting/engine_delegate.h
+++ b/src/script/engine_delegate.h
@@ -1,14 +1,14 @@
 // Aseprite Scripting Library
-// Copyright (c) 2015 David Capello
+// Copyright (c) 2015-2016 David Capello
 //
 // This file is released under the terms of the MIT license.
 // Read LICENSE.txt for more information.
 
-#ifndef SCRIPTING_ENGINE_DELEGATE_H_INCLUDED
-#define SCRIPTING_ENGINE_DELEGATE_H_INCLUDED
+#ifndef SCRIPT_ENGINE_DELEGATE_H_INCLUDED
+#define SCRIPT_ENGINE_DELEGATE_H_INCLUDED
 #pragma once
 
-namespace scripting {
+namespace script {
 
   class EngineDelegate {
   public:
diff --git a/src/scripting/CMakeLists.txt b/src/scripting/CMakeLists.txt
deleted file mode 100644
index e91415d..0000000
--- a/src/scripting/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-# Aseprite Scripting Library
-# Copyright (C) 2015  David Capello
-
-include_directories(${DUKTAPE_DIR})
-
-add_library(scripting-lib
-  engine.cpp)
diff --git a/src/scripting/engine.cpp b/src/scripting/engine.cpp
deleted file mode 100644
index 93d9242..0000000
--- a/src/scripting/engine.cpp
+++ /dev/null
@@ -1,356 +0,0 @@
-// Aseprite Scripting Library
-// Copyright (c) 2015 David Capello
-//
-// This file is released under the terms of the MIT license.
-// Read LICENSE.txt for more information.
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "scripting/engine.h"
-
-#include "base/convert_to.h"
-#include "base/exception.h"
-#include "base/file_handle.h"
-#include "base/memory.h"
-#include "scripting/engine_delegate.h"
-
-#define DUK_OPT_PANIC_HANDLER scripting_engine_panic_handler
-#include <duktape.h>
-
-class ScriptingEngineException : public base::Exception {
-public:
-  ScriptingEngineException(duk_errcode_t code, const char* msg)
-    : Exception(std::string(msg) + " (code " + base::convert_to<std::string>(code) + ")") {
-  }
-};
-
-static void scripting_engine_panic_handler(duk_errcode_t code, const char* msg)
-{
-  throw ScriptingEngineException(code, msg);
-}
-
-extern "C" {
-  #include <duktape.c>
-}
-
-namespace scripting {
-
-bool Context::isConstructorCall()
-{
-  return (duk_is_constructor_call(m_handle) ? true: false);
-}
-
-bool Context::isUndefined(index_t i)
-{
-  return (duk_is_undefined(m_handle, i) ? true: false);
-}
-
-bool Context::isNull(index_t i)
-{
-  return (duk_is_null(m_handle, i) ? true: false);
-}
-
-bool Context::isNullOrUndefined(index_t i)
-{
-  return (duk_is_null_or_undefined(m_handle, i) ? true: false);
-}
-
-bool Context::isBool(index_t i)
-{
-  return (duk_is_boolean(m_handle, i) ? true: false);
-}
-
-bool Context::isNumber(index_t i)
-{
-  return (duk_is_number(m_handle, i) ? true: false);
-}
-
-bool Context::isNaN(index_t i)
-{
-  return (duk_is_nan(m_handle, i) ? true: false);
-}
-
-bool Context::isString(index_t i)
-{
-  return (duk_is_string(m_handle, i) ? true: false);
-}
-
-bool Context::getBool(index_t i)
-{
-  return (duk_get_boolean(m_handle, i) ? true: false);
-}
-
-double Context::getNumber(index_t i)
-{
-  return duk_get_number(m_handle, i);
-}
-
-int Context::getInt(index_t i)
-{
-  return duk_get_int(m_handle, i);
-}
-
-unsigned int Context::getUInt(index_t i)
-{
-  return duk_get_uint(m_handle, i);
-}
-
-const char* Context::getString(index_t i)
-{
-  return duk_get_string(m_handle, i);
-}
-
-bool Context::requireBool(index_t i)
-{
-  return (duk_require_boolean(m_handle, i) ? true: false);
-}
-
-double Context::requireNumber(index_t i)
-{
-  return duk_require_number(m_handle, i);
-}
-
-int Context::requireInt(index_t i)
-{
-  return duk_require_int(m_handle, i);
-}
-
-unsigned int Context::requireUInt(index_t i)
-{
-  return duk_require_uint(m_handle, i);
-}
-
-const char* Context::requireString(index_t i)
-{
-  return duk_require_string(m_handle, i);
-}
-
-void* Context::requireObject(index_t i, const char* className)
-{
-  duk_get_prop_string(m_handle, i, "\xFF" "\xFF" "ptr");
-  void* result = (void*)duk_to_pointer(m_handle, -1);
-  // TODO check pointer type
-  duk_pop(m_handle);
-  return result;
-}
-
-void Context::pushUndefined()
-{
-  duk_push_undefined(m_handle);
-}
-
-void Context::pushNull()
-{
-  duk_push_null(m_handle);
-}
-
-void Context::pushBool(bool val)
-{
-  duk_push_boolean(m_handle, val);
-}
-
-void Context::pushNumber(double val)
-{
-  duk_push_number(m_handle, val);
-}
-
-void Context::pushNaN()
-{
-  duk_push_nan(m_handle);
-}
-
-void Context::pushInt(int val)
-{
-  duk_push_int(m_handle, val);
-}
-
-void Context::pushUInt(unsigned int val)
-{
-  duk_push_uint(m_handle, val);
-}
-
-void Context::pushString(const char* str)
-{
-  duk_push_string(m_handle, str);
-}
-
-void Context::pushThis(void* ptr)
-{
-  duk_push_this(m_handle);
-  duk_push_pointer(m_handle, ptr);
-  duk_put_prop_string(m_handle, -2, "\xFF" "\xFF" "ptr");
-}
-
-void Context::pushObject(void* ptr, const char* className)
-{
-  duk_push_object(m_handle);
-  duk_push_pointer(m_handle, ptr);
-  duk_put_prop_string(m_handle, -2, "\xFF" "\xFF" "ptr");
-
-  duk_get_global_string(m_handle, className);
-  duk_get_prop_string(m_handle, -1, "prototype");
-  duk_set_prototype(m_handle, -3);
-  duk_pop(m_handle);
-}
-
-void* Context::getThis()
-{
-  duk_push_this(m_handle);
-  duk_get_prop_string(m_handle, -1, "\xFF" "\xFF" "ptr");
-  void* result = (void*)duk_to_pointer(m_handle, -1);
-  duk_pop(m_handle);
-  return result;
-}
-
-Engine::Engine(EngineDelegate* delegate)
-  : m_ctx(duk_create_heap_default())
-  , m_delegate(delegate)
-{
-}
-
-Engine::~Engine()
-{
-  duk_destroy_heap(m_ctx.handle());
-}
-
-void Engine::eval(const std::string& jsCode)
-{
-  try {
-    ContextHandle handle = m_ctx.handle();
-
-    duk_eval_string(handle, jsCode.c_str());
-
-    if (!duk_is_null_or_undefined(handle, -1))
-      m_delegate->onConsolePrint(duk_safe_to_string(handle, -1));
-
-    duk_pop(handle);
-  }
-  catch (const std::exception& ex) {
-    std::string err = "Error: ";
-    err += ex.what();
-    m_delegate->onConsolePrint(err.c_str());
-  }
-}
-
-void Engine::evalFile(const std::string& file)
-{
-  try {
-    ContextHandle handle = m_ctx.handle();
-
-    base::FileHandle fhandle(base::open_file(file, "rb"));
-    FILE* f = fhandle.get();
-    if (!f)
-      return;
-
-    if (fseek(f, 0, SEEK_END) < 0)
-      return;
-
-    int sz = ftell(f);
-    if (sz < 0)
-      return;
-
-    if (fseek(f, 0, SEEK_SET) < 0)
-      return;
-
-    char* buf = (char*)duk_push_fixed_buffer(handle, sz);
-    ASSERT(buf != nullptr);
-    if (fread(buf, 1, sz, f) != sz)
-      return;
-
-    fclose(f);
-    f = nullptr;
-
-    duk_push_string(handle, duk_to_string(handle, -1));
-    duk_eval_raw(handle, nullptr, 0, DUK_COMPILE_EVAL);
-
-    if (!duk_is_null_or_undefined(handle, -1))
-      m_delegate->onConsolePrint(duk_safe_to_string(handle, -1));
-
-    duk_pop(handle);
-  }
-  catch (const std::exception& ex) {
-    std::string err = "Error: ";
-    err += ex.what();
-    m_delegate->onConsolePrint(err.c_str());
-  }
-}
-
-void Engine::registerFunction(const char* id, Function function, int nargs)
-{
-  ContextHandle handle = m_ctx.handle();
-
-  duk_push_global_object(handle);
-  duk_push_c_function(handle, function, nargs);
-  duk_put_prop_string(handle, -2, id);
-}
-
-void Engine::registerClass(const char* id,
-                           Function ctorFunc, int ctorNargs,
-                           const FunctionEntry* methods,
-                           const PropertyEntry* props)
-{
-  ContextHandle handle = m_ctx.handle();
-  duk_push_c_function(handle, ctorFunc, ctorNargs);
-
-  duk_push_object(handle);
-
-  if (methods)
-    duk_put_function_list(handle, -1, (const duk_function_list_entry*)methods);
-
-  if (props) {
-    for (int i=0; props[i].key; ++i) {
-      duk_push_string(handle, props[i].key);
-
-      duk_idx_t objidx = -2;
-      duk_uint_t flags = 0;
-
-      if (props[i].getter) {
-        flags |= DUK_DEFPROP_HAVE_GETTER;
-        duk_push_c_function(handle, props[i].getter, 0);
-        --objidx;
-      }
-
-      if (props[i].setter) {
-        flags |= DUK_DEFPROP_HAVE_SETTER;
-        duk_push_c_function(handle, props[i].setter, 1);
-        --objidx;
-      }
-
-      duk_def_prop(handle, objidx, flags);
-    }
-  }
-
-  duk_put_prop_string(handle, -2, "prototype");
-
-  duk_put_global_string(handle, id);
-}
-
-void Engine::registerGlobal(const char* id,
-                            Function getter, Function setter)
-{
-  ContextHandle handle = m_ctx.handle();
-
-  duk_push_global_object(handle);
-  duk_push_string(handle, id);
-
-  duk_idx_t objidx = -2;
-  duk_uint_t flags = 0;
-
-  if (getter) {
-    flags |= DUK_DEFPROP_HAVE_GETTER;
-    duk_push_c_function(handle, getter, 0);
-    --objidx;
-  }
-
-  if (setter) {
-    flags |= DUK_DEFPROP_HAVE_SETTER;
-    duk_push_c_function(handle, setter, 1);
-    --objidx;
-  }
-
-  duk_def_prop(handle, objidx, flags);
-}
-
-} // namespace scripting
diff --git a/third_party/duktape b/third_party/duktape
index 90090c7..0de771c 160000
--- a/third_party/duktape
+++ b/third_party/duktape
@@ -1 +1 @@
-Subproject commit 90090c7f9277e192af58e1878aee24e5d9f48eaa
+Subproject commit 0de771cd55df729ec8a881b601ca3d4389e4a69a

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git



More information about the Pkg-games-commits mailing list