diff --git a/README.md b/README.md index ff6ba2ec1..9e96adbb0 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 a469391f7..81eace42a 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 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 000000000..d0ce3609a --- /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 + +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/script/app_object.h b/src/app/script/app_object.h new file mode 100644 index 000000000..528ae43eb --- /dev/null +++ b/src/app/script/app_object.h @@ -0,0 +1,22 @@ +// 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_APP_OBJECT_H_INCLUDED +#define APP_SCRIPT_APP_OBJECT_H_INCLUDED +#pragma once + +namespace script { + class Context; +} + +namespace app { + + void register_app_object(script::Context& ctx); + +} // namespace app + +#endif diff --git a/src/app/script/app_scripting.cpp b/src/app/script/app_scripting.cpp new file mode 100644 index 000000000..db8c48b6d --- /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 37abf827c..ac89a43d4 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 000000000..dc7979240 --- /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 + +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/script/console_object.h b/src/app/script/console_object.h new file mode 100644 index 000000000..bd62a5610 --- /dev/null +++ b/src/app/script/console_object.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef APP_SCRIPT_CONSOLE_OBJECT_H_INCLUDED +#define APP_SCRIPT_CONSOLE_OBJECT_H_INCLUDED +#pragma once + +namespace script { + class Context; +} + +namespace app { + + void register_console_object(script::Context& ctx); + +} // namespace app + +#endif diff --git a/src/app/script/pixel_color.h b/src/app/script/pixel_color.h new file mode 100644 index 000000000..09e376dce --- /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 000000000..83958616e --- /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 +#include + +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::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256)); + base::UniquePtr 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 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 000000000..98ab6e2d5 --- /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 386ab50cc..000000000 --- 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 1bfbb1c51..000000000 --- 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 dceafa05e..000000000 --- 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::createBasicSprite((doc::PixelFormat)colorMode, w, h, 256)); - base::UniquePtr 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 1bf14e604..122541022 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 #include @@ -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 21e31ca6d..cb7f3d784 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 9bcd0b02f..3fd60de0f 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 000000000..783d7d61a --- /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 11f1e5e40..24fd48372 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 3862e85a7..2bf61ab33 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 000000000..2eae72a82 --- /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 +#include + +#include + +class ScriptEngineException : public base::Exception { +public: + ScriptEngineException(duk_errcode_t code, const char* msg) + : Exception(std::string(msg) + " (code " + base::convert_to(code) + ")") { + } +}; + +namespace script { + +namespace { + +// TODO classes in modules isn't supported yet +std::map 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 992eb7e06..c1ff9de11 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 -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); - typedef void* ContextHandle; + struct FunctionEntry { + const char* id; + Function value; + index_t nargs; + }; + + struct PropertyEntry { + const char* id; + Function getter; + Function setter; + }; + + 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 b4e64b79e..b83514178 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 e91415d36..000000000 --- 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 93d9242cb..000000000 --- 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 - -class ScriptingEngineException : public base::Exception { -public: - ScriptingEngineException(duk_errcode_t code, const char* msg) - : Exception(std::string(msg) + " (code " + base::convert_to(code) + ")") { - } -}; - -static void scripting_engine_panic_handler(duk_errcode_t code, const char* msg) -{ - throw ScriptingEngineException(code, msg); -} - -extern "C" { - #include -} - -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 90090c7f9..0de771cd5 160000 --- a/third_party/duktape +++ b/third_party/duktape @@ -1 +1 @@ -Subproject commit 90090c7f9277e192af58e1878aee24e5d9f48eaa +Subproject commit 0de771cd55df729ec8a881b601ca3d4389e4a69a