Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

javascript objects as userdata #9

Open
daurnimator opened this issue Jan 16, 2014 · 1 comment
Open

javascript objects as userdata #9

daurnimator opened this issue Jan 16, 2014 · 1 comment

Comments

@daurnimator
Copy link

Native javascript objects could be represented as userdata
This requires userdata behaviour to actually be respected (in regards to metatables and more)

A few changes I've started making to facilitate this:

diff --git a/vm/moonshine.js b/vm/moonshine.js
index 1b2fc37..94f61ca 100644
--- a/vm/moonshine.js
+++ b/vm/moonshine.js
@@ -1380,6 +1380,18 @@ shine.Closure.prototype.dispose = function (force) {
        } else if (typeof b == 'string' && shine.lib.string[c]) {
            result = shine.lib.string[c];

+       } else if (b.__shine) {
+           var mt, __index;
+           if ( (mt = b.__shine.metatable) && (__index = mt.getMember("__index"))) {
+               if (__index instanceof shine.Table) {
+                   result = __index.getMember(c);
+               } else {
+                   result = __index(b, c);
+               }
+           } else {
+               throw new shine.Error('Attempt to index a userdata value');
+           }
+
        } else {
            result = b[c];
        }
@@ -3517,12 +3529,12 @@ var shine = shine || {};
        tostring: function (e) {
            var mt, mm;

-           if (e !== undefined && e instanceof shine.Table && (mt = e.__shine.metatable) && (mm = mt.getMember('__tostring'))) return mm.call(mm, e);
+           if (e !== undefined && typeof e == "object" && (mt = e.__shine.metatable) && (mm = mt.getMember('__tostring'))) return mm.call(mm, e);

            if (e instanceof shine.Table || e instanceof shine.Function) return e.toString();
            if (typeof e == 'function') return 'function: [host code]';

-           return shine.utils.coerce(e, 'string') || 'userdata';
+           return shine.utils.coerce(e, 'string');
        },

And a better DOMAPI with consistent metatables

diff --git a/extensions/DOMAPI/DOMAPI.moonshine.js b/extensions/DOMAPI/DOMAPI.moonshine.js
index 1c5867b..b2304ff 100644
--- a/extensions/DOMAPI/DOMAPI.moonshine.js
+++ b/extensions/DOMAPI/DOMAPI.moonshine.js
@@ -21,127 +21,101 @@
  */

 (function (shine) {
-
-   function jsToLua (obj) {
-       var t, mt;
-
-       mt = new shine.Table({
-
+   var object_mt = new shine.Table({
        __index: function (t, key) {
-               var property = obj[key];
-
-               // Bind methods to object and convert args and return values
-               if (typeof property == 'function' || (property && property.prototype && typeof property.prototype.constructor == 'function')) { // KLUDGE: Safari reports native constructors as objects, not functions :-s
-                   var f = function () {
-                       var args = convertArguments(arguments, luaToJS),
-                           retval = property.apply(args.shift(), args);
-
-                       if (typeof retval == 'object') return jsToLua(retval);
-                       return [retval];
-                   };
-
-                   // Add a new method for instantiating classes
-                   f.new = function () { 
-                       var args = convertArguments(arguments, luaToJS),
-                           argStr,
-                           obj,
-                           i, l;
-
-                       argStr = (l = args.length)? 'args[0]' : '';
-                       for (i = 1; i < l; i++) argStr += ',args[' + i + ']';
-
-                       obj = eval('new property(' + argStr + ')');
-                       return jsToLua(obj);
-                   };
-
-                   return f;
-               }
-
-               // Recurse down properties
-               if (typeof property == 'object') return jsToLua(property);
-
-               // Return primatives as is
-               return property;
+           return jsToLua(t.__native[key]);
        },
-
-
        __newindex: function (t, key, val) {
-               obj[key] = luaToJS(val);
+           t.__native[key] = luaToJS(val);
+       },
+       __tostring: function (t) {
+           console.log(t,t.__native)
+           return "userdata: " + toString(t.__native);
        }
-
    });

-       mt.source = obj;
-
-
-       t = new shine.Table();
-       shine.gc.incrRef(t);
+   var function_mt = new shine.Table({
+       __call: function(t) {
+           var args = Array.prototype.slice.call(arguments,1).map(luaToJS);
+           return [jsToLua(t.__native.apply(null, args))];
+       },
+       __new: function(t) {
+           var args = Array.prototype.slice.call(arguments,1).map(luaToJS);
+           return [jsToLua(new t._constructor(args))];
+       },
+       __index: object_mt.__index,
+       __newindex: object_mt.__newindex,
+       __tostring: object_mt.__tostring
+   });

-       // Return proxy table
-       return shine.lib.setmetatable(t, mt);
+   function jsToLua (obj) {
+       switch (typeof obj) {
+           case 'undefined':
+           case 'number':
+           case 'boolean':
+           case 'string':
+               return obj;
+           case 'object':
+               if (obj instanceof Number ||
+                   obj instanceof Boolean ||
+                   obj instanceof String) {
+                   return obj.valueOf();
                }
+               return {
+                   __shine: {
+                       metatable: object_mt
+                   },
+                   __native: obj
+               };
+           case 'function':
+               function constructor(args) {
+                   return obj.apply(this, args);
+               }
+               constructor.prototype = obj.prototype;

-
-
+               return {
+                   __shine: {
+                       metatable: function_mt
+                   },
+                   __native: obj,
+                   __constructor: constructor
+               };
+           default:
+               throw "Unable to convert to js value to lua";
+       }
+   }

    function luaToJS (val) {
-       var mt;
-
-       // Make shine.Functions invokable
+       switch (typeof val) {
+           case 'undefined':
+           case 'number':
+           case 'boolean':
+           case 'string':
+           case 'function':
+               return val;
+           case 'object':
                if (val instanceof shine.Function) {
                    return function () {
-               return jsToLua(val.apply(undefined, convertArguments(arguments, jsToLua)));
+                       var args = Array.prototype.slice.call(arguments,0).map(jsToLua);
+                       return jsToLua(val.apply(void 0, args));
                    };
+               } else if (val instanceof shine.Table) {
+                   // TODO: reverse proxy
+                   return shine.utils.toObject(val);
+               } else {
+                   switch(val.__shine) {
+                       case function_mt:
+                       case object_mt:
+                           return val.__native;
+                       default:
+                           throw "Unable to convert userdata object";
                    }
-
-       if (val instanceof shine.Table) {
-           // If object has been wrapped by jsToLua(), use original object instead
-           if ((mt = shine.lib.getmetatable(val)) && mt.source) return mt.source;
-
-           // Else iterate over table
-           var isArr = shine.lib.table.getn(val) > 0,
-               result = shine.gc['create' + (isArr? 'Array' : 'Object')](),
-               numValues = val.__shine.numValues,
-               i,
-               l = numValues.length;
-
-           for (i = 1; i < l; i++) {
-               result[i - 1] = ((numValues[i] || shine.EMPTY_OBJ) instanceof shine.Table)? luaToJS(numValues[i]) : numValues[i];
                }
-
-           for (i in val) {
-               if (val.hasOwnProperty(i) && !(i in shine.Table.prototype) && i !== '__shine') {
-                   result[i] = ((val[i] || shine.EMPTY_OBJ) instanceof shine.Table)? luaToJS(val[i]) : val[i];
+           default:
+               throw "How did this get in the lua vm?"
        }
    }

-           return result;
-       }
-
-
-       // Convert tables to objects
-       if (typeof val == 'object') return shine.utils.toObject(val);
-
-       // return primatives as is
-       return val;
-   }
-
-
-
-
-   function convertArguments (arguments, translateFunc) {
-       var args = [], i, l;
-
-       for (i = 0, l = arguments.length; i < l; i++) {
-           args.push(translateFunc(arguments[i]));
-       }
-
-       return args;
-   };
-
-
-
-
    // Create wrapped window API
    shine.DOMAPI = { window: jsToLua(window) };
@greatwolf
Copy link

Wouldn't it be easier to fork it and send a pull/merge request back to here instead of posting the diff as an issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants