nerdneilsfield / pyv8

Automatically exported from code.google.com/p/pyv8
0 stars 0 forks source link

Cannot use arbitrary callable for .valueOf() #187

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

Run the following code:

import PyV8

class Foober(object):
    def __init__(self, x):
        self.x = x 
    def __call__(self):
        return self.x

class Foo(object):
    def __init__(self):
        self.toString = Foober("Hi")

class Global(object):
    def Foo(self):
        return Foo()

ctxt = PyV8.JSContext(Global())
with ctxt:
    print ctxt.eval('"Say " + Foo()');

What is the expected output? What do you see instead?

I expect to see "Say Hi", but instead I get 

TypeError: TypeError: Cannot convert object to primitive value (  @ 1 : 7 )  -> 
"Say " + Foo()

What version of the product are you using? On what operating system?

Python 2.6.6, PyV8 r443, Windows 7.

Please provide any additional information below.

Probably checking for function instead of callable() when looking for 
toString/valueOf/etc.

Original issue reported on code.google.com by csaft...@gmail.com on 17 Jul 2013 at 10:31

GoogleCodeExporter commented 9 years ago
In fact, PyV8 doesn't make the decision, v8 only allow function for toString 
and valueOf

// runtime.js
// ECMA-262, section 8.6.2.6, page 28.
function DefaultString(x) {
  var toString = x.toString;
  if (IS_SPEC_FUNCTION(toString)) {
    var s = %_CallFunction(x, toString);
    if (%IsPrimitive(s)) return s;
  }

  var valueOf = x.valueOf;
  if (IS_SPEC_FUNCTION(valueOf)) {
    var v = %_CallFunction(x, valueOf);
    if (%IsPrimitive(v)) return v;
  }

  throw %MakeTypeError('cannot_convert_to_primitive', []);
}

// macros.py
# Macro for ECMAScript 5 queries of the type:
# "IsCallable(O)"
# We assume here that this is the same as being either a function or a function
# proxy. That ignores host objects with [[Call]] methods, but in most situations
# we cannot handle those anyway.
macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function');

You could just wrap your object with lambda 

class Foo(object):
    def __init__(self):
        self.toString = lambda: Foober("Hi")()

Original comment by flier...@gmail.com on 12 Aug 2013 at 8:26

GoogleCodeExporter commented 9 years ago
Hmm, interesting... do you think it would be worth it to make any callable() 
python object map to a Function with properties? In Python a 'function' is just 
an object with a __call__ property, after all... why should functions, unbound 
instance methods, bound instance methods, and classes all be usable but not 
classes with a __call__ method?

Original comment by csaft...@gmail.com on 12 Aug 2013 at 7:10

GoogleCodeExporter commented 9 years ago
As you know, every Python object will be wrapped as a JS object to V8 side, the 
type can't be both [object] and [function], so I think use a lambda in Python 
side will be acceptable.

Original comment by flier...@gmail.com on 14 Aug 2013 at 2:58

GoogleCodeExporter commented 9 years ago
But of course! It seems to me it would be better to have Python objects that 
have a __call__ method be [function], and other non-number etc. types to be 
[object] - this would also resolve issue #191 - but it's ultimately up to you! 
I admit it is not such a big deal. This only really came up because I was 
trying to work around the memory issues, but without those issues, there's no 
reason these would matter in particular.

Original comment by csaft...@gmail.com on 14 Aug 2013 at 4:40