svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.92k stars 514 forks source link

ES2015 Class Support #276

Open kphillisjr opened 9 years ago

kphillisjr commented 9 years ago

This is one of the more advanced ES6 Features. I also believe this is probably an area where Duktape can definitely make some vast improvements. The examples for this are from the es6 Features website and more information can be found on the 2ality blog.

kphillisjr commented 9 years ago

After a little bit of thought, I figured I could outline some example C code for use by applications implementing this feature. Keep in mind this is mostly end user API and does not define any actual source changes to implement this though.

Minor changes which can be of use now.

New Functions

// new Generalized functions.
void duk_get_constructor(duk_context *ctx, duk_idx_t index);
void duk_set_constructor(duk_context *ctx, duk_idx_t index);

These functions work the same as duk_get_finalizer and duk_set_finalizer but are intended to make it easier for a program to access a constructor.

Example Base Class:

New Functions

duk_idx_t duk_push_c_class(duk_context *ctx, const char* base);

This Function works a lot like your duk_push_object, and the base parameter can be set to NULL (0) when this class does not inherit from anything.

Class Source

/* These Functions usually will go in a Header */
duk_ret_t duk_class_shape_constructor(duk_context *ctx);
duk_ret_t duk_class_shape_finalizer(duk_context *ctx);
duk_ret_t duk_class_shape_staticFunc(duk_context *ctx);

duk_idx_t duk_register_shape_class(duk_context *ctx)
{
    /* Creat Shape Base Class */
    duk_idx_t shape_idx;
    duk_push_global_object(ctx); // Push Global Space.
    shape_idx = duk_push_c_class(ctx, 0);
    duk_push_c_function(ctx, duk_class_shape_constructor, 3 /*nargs*/);
    duk_set_constructor(ctx, shape_idx);
    duk_push_c_function(ctx, duk_class_shape_finalizer, 1 /*nargs*/);
    duk_set_finalizer(ctx, shape_idx);
    /* Define a Static Member */
    duk_push_c_function(ctx, duk_class_shape_default,0);
    duk_put_prop_string(ctx, shape_idx, "createDefaultShape");
    duk_put_prop_string(ctx, -1, "Shape"); // Class is now defined as Shape.
    return shape_idx;
}

duk_ret_t duk_class_shape_constructor(duk_context *ctx)
{
    int id = duk_require_int(ctx,0);
    int x = duk_require_int(ctx,1);
    int y = duk_require_int(ctx,2);
    /* This Constructor assumes that the object is already Created */
    /* Current Method requires a call to duk_push_object */
    duk_push_this(ctx);
    duk_push_int(ctx, id);
    duk_put_prop_string(ctx, obj_idx, "id");
    duk_push_int(ctx,x)
    duk_put_prop_string(ctx, obj_idx, "x");
    duk_push_int(ctx,y)
    duk_put_prop_string(ctx, obj_idx, "y");
    duk_pop(ctx);
    return 0;
}
duk_ret_t duk_class_shape_finalizer(duk_context *ctx)
{
/* Take care of New object Clean up Here */
return 0;
}
duk_ret_t duk_class_shape_staticFunc(duk_context *ctx)
{
/* Do something that is normally done in static functions */
return 0;
}

Example Inherited Class:

New Functions

duk_bool_t duk_has_parent(duk_context *ctx); // super is set.
void duk_push_parent(duk_context *ctx); // Pushes Parent to top of stack.

duk_has_parent - returns false if parent is set to a value other than Null. duk_push_parent - This works a lot like duk_push_object, but instead sets the parent object to the top of the stack.

Class Source

duk_ret_t duk_class_rectangle_constructor(duk_context *ctx);
duk_ret_t duk_class_rectangle_finalizer(duk_context *ctx);
duk_ret_t duk_class_rectangle_default(duk_context *ctx);

duk_idx_t duk_register_rectangle_class(duk_context *ctx)
{
    /* Create Rectangle Class */
    duk_idx_t rectangle_idx;
    duk_push_global_object(ctx); // Push Global Space.
    rectangle_idx = duk_push_c_class(ctx, "Shape");
    duk_push_c_function(ctx, duk_class_rectangle_constructor, 5 /*nargs*/);
    duk_set_constructor(ctx, rectangle_idx);
    duk_push_c_function(ctx, duk_class_rectangle_finalizer, 1 /*nargs*/);
    duk_set_finalizer(ctx, rectangle_idx);
    /* Define a Static Member */
    duk_push_c_function(ctx, duk_class_rectangle_default,0);
    duk_put_prop_string(ctx, rectangle_idx, "createDefaultRectangle");
    duk_put_prop_string(ctx, -1, "Rectangle"); // Class is now defined as Rectangle.
    return rectangle_idx;
}

duk_ret_t duk_class_rectangle_constructor(duk_context *ctx)
{
    int id = duk_require_int(ctx,0);
    int x = duk_require_int(ctx,1);
    int y = duk_require_int(ctx,2);
    int w = duk_require_int(ctx,3);
    int h = duk_require_int(ctx,4);
    duk_push_this(ctx);
    if(duk_has_parent(ctx)) {
        /* Call parent Constructor */
        duk_push_parent(ctx);
        duk_get_constructor(ctx, -1);
        duk_push_int(ctx, id);
        duk_push_int(ctx,x)
        duk_push_int(ctx,y)
        duk_call(ctx); 
        duk_pop(ctx); // Done with Parent.
    }
    duk_push_int(w);
    duk_put_prop_string(ctx, -2, "width");
    duk_push_int(h);
    duk_put_prop_string(ctx, -2, "height");
    /* Take care of New object Here */
    return 0;
}
duk_ret_t duk_class_rectangle_finalizer(duk_context *ctx)
{
    /* Take care of Current Object clean up */
    return 0;
}
duk_ret_t duk_class_rectangle_default(duk_context *ctx)
{
    /* Do something that is normally done in static functions */
    return 0;
}
s1341 commented 8 years ago

Is this expected to land any time soon? This feature would make my life a whole lot easier.

kphillisjr commented 8 years ago

I completely forgot I made this bug report. Anyways, I recently noticed another stack based scripting language that makes use of classes that has some basic functionally similar what ECMAScript 6 classes require. This language is called Squirrel. I probably should mention that the licensing on this is compatible with Duktape's License because it also uses the MIT License.

s1341 commented 8 years ago

@svaarala any chance we'll see this in duktape any time soon?

svaarala commented 8 years ago

@s1341 There's no set plan for adding ES6 features yet. ES6 will require some groundwork in the lexer and compiler before it'll be possible to implement ES6 features comfortably.

Kernell commented 8 years ago

@svaarala, Node.js and SpiderMonkey already have this feature.

The normal syntax for class declarations - this is something that is very lacking in the JavaScript.

brodycj commented 8 years ago

Why not use an ES6 transpiler such as Babel JS?

Kernell commented 8 years ago

@brodybits, This is inconvenient workaround

rosshadden commented 7 years ago

@brodybits You might be just asking about using babel to solve only this specific issue/request, but I am going to assume you really mean it in general, for a lot of the opened ES6 feature request issues. In general, even though transpilers can solve a lot of these ES6 requests, there are several things to take into consideration:

One is that transpiled solutions for certain features can be slightly slower to execute at runtime than if the engine supported them natively. This issue is a bad example of that, of course, but features like default arguments, destructuring, rest params, and things like Array#from make better cases. The strongest cases I can think of at the moment are Sets and Maps, for which the native implementation would be much faster than anything spit out by a transpiler.

Another consideration is that a lot of things can't be transpiled or even polyfilled properly, and thus native solutions must exist for support to be present.

Lastly, some users of duktape are unable to use transpilers (as pointed out by @ricardobeat in #273), so implementing

svaarala commented 7 years ago

@rosshadden Fully agree that transpiling is not ideal, and there's no disagreement whether full ES2015 support should be provided (it is intended to be eventually fully supported). However, the changes needed are not trivial and take time to implement, especially those requiring compiler rework (like destructuring).

rosshadden commented 7 years ago

Haha yeah, of course! I was just trying to answer what I envisioned as the generalized version of his question, shedding some light in places that not everyone might know about. For instance, I did not even consider use cases where people were unable to use a transpiler until I read #273.

svaarala commented 7 years ago

Covering transpiling in practice (including its limitations) would make a good Wiki Howto BTW.

Anyway, just wanted to make clear supporting ES2015 is a project goal. It's not a given because some ES2015 features conflict a bit with keeping a low footprint and memory usage. In practice the approach will be such that the low memory baseline is an ES5-like configuration where you can enable individual ES2015 features that you specifically need. However, even with that approach some things like compiler architecture potentially conflict with footprint goals.

So there are definitely challenges ahead (which is good :-). In the meanwhile ES2015 features have been implemented little by little where they don't require significant structural rework. For example, 2.0.0 adds support for Reflect, Symbols, more Math built-ins, new object literal property formats, hex/octal literals, etc.

svaarala commented 7 years ago

Added this for the Wiki: https://github.com/svaarala/duktape-wiki/issues/157.

fatcerberus commented 7 years ago

@svaarala I wonder how difficult it would be to implement classes with the current parser. My initial thought is that it shouldn't be too different from parsing an object literal (and hence require minimal hacking, which I know is something you want to avoid), but there are probably complications that I'm not accounting for.

ePirat commented 5 years ago

Whats the status of this, I guess this is still a missing feature?

kingdevnl commented 4 years ago

Wil this ever be implemented?

svaarala commented 4 years ago

Provided that I (or others) have time to spend on the implementation, it's definitely a goal. Please note that this project is (originally) a personal off-hours project. It has no commercial backing, and no dedicated development resources, so progress depends entirely on the free time available.

kingdevnl commented 4 years ago

True but this issue has almost been there for 5 years hahaha

Kikasuru commented 3 years ago

Bump, any updates on this?