-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
698 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
This package provides a bridge between WebKit and a PyPy-based interpreter. | ||
|
||
To use, please create a symlink to the WebKit build as `webkit`. It should | ||
contain a debug build and libJavaScriptCore.so. To create libJavaScriptCore.so, | ||
go to WebKitBuild/Debug/.libs and perform: | ||
|
||
$ ar x libJavaScriptCore.a | ||
$ g++ -shared -o libJavaScriptCore.so *.o -lpthread -lglib-2.0 `icu-config --ldflags` | ||
|
||
And, finally, make sure the .so is on your LD_LIBRARY_PATH. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# No Copyright (-) 2009-2010 The Ampify Authors. This file is under the | ||
# Public Domain license that can be found in the root LICENSE file. | ||
|
||
"""A webkit bridge for PyPy-based interpreters.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# No Copyright (-) 2009-2010 The Ampify Authors. This file is under the | ||
# Public Domain license that can be found in the root LICENSE file. | ||
|
||
from pypy.conftest import Directory, Module, option, ConftestPlugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
# No Copyright (-) 2009-2010 The Ampify Authors. This file is under the | ||
# Public Domain license that can be found in the root LICENSE file. | ||
|
||
from pypy.interpreter.baseobjspace import Wrappable, W_Root, ObjSpace | ||
from pypy.interpreter.gateway import interp2app | ||
from pypy.interpreter.typedef import TypeDef | ||
from pypy.interpreter.error import OperationError | ||
from webkit_bridge.webkit_rffi import * | ||
from pypy.rpython.lltypesystem import lltype, rffi | ||
from pypy.interpreter.argument import Arguments | ||
from pypy.rpython.memory.support import AddressDict | ||
|
||
|
||
class JavaScriptContext(object): | ||
|
||
def __init__(self, space): | ||
self._ctx = lltype.nullptr(JSValueRef.TO) | ||
self.space = space | ||
self.w_js_exception = space.appexec([], '''(): | ||
class JSException(Exception): | ||
pass | ||
return JSException | ||
''') | ||
# note. It's safe to cast pointers in this dict to ints | ||
# as they're non-movable (raw) ones | ||
self.applevel_callbacks = {} | ||
def callback(ctx, js_function, js_this, js_args): | ||
arguments = Arguments(space, | ||
[self.js_to_python(arg) for arg in js_args]) | ||
w_callable = self.applevel_callbacks.get( | ||
rffi.cast(lltype.Signed, js_function), None) | ||
if w_callable is None: | ||
raise Exception("Got wrong callback, should not happen") | ||
w_res = space.call_args(w_callable, arguments) | ||
return self.python_to_js(w_res) | ||
self.js_callback_factory = create_js_callback(callback) | ||
|
||
def python_to_js(self, w_obj): | ||
space = self.space | ||
if space.is_w(w_obj, space.w_None): | ||
return JSValueMakeUndefined(self._ctx) | ||
elif space.is_true(space.isinstance(w_obj, space.w_bool)): | ||
return JSValueMakeBoolean(self._ctx, space.is_true(w_obj)) | ||
elif space.is_true(space.isinstance(w_obj, space.w_int)): | ||
return JSValueMakeNumber(self._ctx, space.int_w(w_obj)) | ||
elif space.is_true(space.isinstance(w_obj, space.w_float)): | ||
return JSValueMakeNumber(self._ctx, space.float_w(w_obj)) | ||
elif space.is_true(space.isinstance(w_obj, space.w_str)): | ||
return JSValueMakeString(self._ctx, self.newstr(space.str_w(w_obj))) | ||
elif isinstance(w_obj, JSObject): | ||
return w_obj.js_val | ||
elif space.is_true(space.callable(w_obj)): | ||
name = space.str_w(space.getattr(w_obj, space.wrap('__name__'))) | ||
js_func = self.js_callback_factory(self._ctx, name) | ||
self.applevel_callbacks[rffi.cast(lltype.Signed, js_func)] = w_obj | ||
return js_func | ||
else: | ||
raise NotImplementedError() | ||
|
||
def js_to_python(self, js_obj, this=NULL): | ||
space = self.space | ||
tp = JSValueGetType(self._ctx, js_obj) | ||
if tp == kJSTypeUndefined: | ||
return space.w_None | ||
elif tp == kJSTypeNull: | ||
return space.w_None | ||
elif tp == kJSTypeBoolean: | ||
return space.wrap(JSValueToBoolean(self._ctx, js_obj)) | ||
elif tp == kJSTypeNumber: | ||
return space.wrap(JSValueToNumber(self._ctx, js_obj)) | ||
elif tp == kJSTypeString: | ||
return space.wrap(self.str_js(js_obj)) | ||
elif tp == kJSTypeObject: | ||
return space.wrap(JSObject(self, js_obj, this)) | ||
else: | ||
raise NotImplementedError(tp) | ||
|
||
def newstr(self, s): | ||
return JSStringCreateWithUTF8CString(s) | ||
|
||
def str_js(self, js_s): | ||
return JSStringGetUTF8CString(JSValueToString(self._ctx, js_s)) | ||
|
||
def get(self, js_obj, name): | ||
return JSObjectGetProperty(self._ctx, js_obj, self.newstr(name)) | ||
|
||
def set(self, js_obj, name, js_val): | ||
js_name = JSStringCreateWithUTF8CString(name) | ||
JSObjectSetProperty(self._ctx, js_obj, js_name, js_val, 0) | ||
|
||
def eval(self, s, this=NULL): | ||
return JSEvaluateScript(self._ctx, s, this) | ||
|
||
def call(self, js_val, args, this=NULL): | ||
try: | ||
return JSObjectCallAsFunction(self._ctx, js_val, this, args) | ||
except JSException, e: | ||
raise OperationError(self.w_js_exception, self.space.wrap(e.repr())) | ||
|
||
def propertylist(self, js_val): | ||
return JSPropertyList(self._ctx, js_val) | ||
|
||
def globals(self): | ||
return JSObject(self, JSContextGetGlobalObject(self._ctx)) | ||
|
||
|
||
class JSObject(Wrappable): | ||
|
||
def __init__(self, ctx, js_val, this=NULL): | ||
self.ctx = ctx | ||
self.js_val = js_val | ||
self.this = this | ||
|
||
def descr_get(self, space, w_name): | ||
name = space.str_w(w_name) | ||
if name == '__dict__': | ||
proplist = self.ctx.propertylist(self.js_val) | ||
w_d = space.newdict() | ||
for name in proplist: | ||
w_item = self.ctx.js_to_python(self.ctx.get(self.js_val, name)) | ||
space.setitem(w_d, space.wrap(name), w_item) | ||
return w_d | ||
js_val = self.ctx.get(self.js_val, name) | ||
return self.ctx.js_to_python(js_val, self.js_val) | ||
|
||
def descr_set(self, space, w_name, w_value): | ||
name = space.str_w(w_name) | ||
if name == '__dict__': | ||
raise OperationError(space.w_ValueError, | ||
space.wrap("Cannot change __dict__")) | ||
js_val = self.ctx.python_to_js(w_value) | ||
self.ctx.set(self.js_val, name, js_val) | ||
return space.w_None | ||
|
||
def call(self, space, args_w): | ||
js_res = self.ctx.call(self.js_val, [self.ctx.python_to_js(arg) | ||
for arg in args_w], self.this) | ||
return self.ctx.js_to_python(js_res) | ||
|
||
def str(self, space): | ||
return space.wrap('JSObject(' + self.ctx.str_js(self.js_val) + ')') | ||
|
||
JSObject.typedef = TypeDef("JSObject", | ||
__getattribute__ = interp2app(JSObject.descr_get, | ||
unwrap_spec=['self', ObjSpace, W_Root]), | ||
__getitem__ = interp2app(JSObject.descr_get, | ||
unwrap_spec=['self', ObjSpace, W_Root]), | ||
__setitem__ = interp2app(JSObject.descr_set, | ||
unwrap_spec=['self', ObjSpace, W_Root, W_Root]), | ||
__setattr__ = interp2app(JSObject.descr_set, | ||
unwrap_spec=['self', ObjSpace, W_Root, W_Root]), | ||
__str__ = interp2app(JSObject.str, | ||
unwrap_spec=['self', ObjSpace]), | ||
__call__ = interp2app(JSObject.call, | ||
unwrap_spec=['self', ObjSpace, 'args_w']) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# No Copyright (-) 2009-2010 The Ampify Authors. This file is under the | ||
# Public Domain license that can be found in the root LICENSE file. |
112 changes: 112 additions & 0 deletions
112
Library/thepianpython/webkit_bridge/test/test_applevel.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
# No Copyright (-) 2009-2010 The Ampify Authors. This file is under the | ||
# Public Domain license that can be found in the root LICENSE file. | ||
|
||
from pypy.conftest import gettestobjspace | ||
from webkit_bridge.jsobj import JSObject, JavaScriptContext | ||
from webkit_bridge.webkit_rffi import (JSGlobalContextCreate, | ||
JSGlobalContextRelease, | ||
empty_object) | ||
from pypy.interpreter.gateway import interp2app | ||
|
||
|
||
class AppTestBindings(object): | ||
|
||
def setup_class(cls): | ||
ctx = JavaScriptContext(cls.space) | ||
ctx._ctx = JSGlobalContextCreate() | ||
cls.w_js_obj = cls.space.wrap(JSObject(ctx, ctx.eval('[]'))) | ||
this = ctx.eval('[]') | ||
ctx.eval('this.x = function(a, b) { return(a + b); }', this) | ||
cls.w_func = cls.space.wrap(JSObject(ctx, ctx.get(this, 'x'))) | ||
cls.w_js_obj_2 = cls.space.wrap(JSObject(ctx, ctx.eval('[]'))) | ||
|
||
def interpret(source): | ||
return ctx.js_to_python(ctx.eval(source)) | ||
|
||
space = cls.space | ||
cls.w_interpret = space.wrap(interp2app(interpret, unwrap_spec=[str])) | ||
cls.w_globals = ctx.globals() | ||
cls.w_JSException = ctx.w_js_exception | ||
|
||
def test_getattr_none(self): | ||
assert self.js_obj.x == None | ||
|
||
def test_getsetattr_obj(self): | ||
self.js_obj['x'] = 3 | ||
assert isinstance(self.js_obj.x, float) | ||
assert self.js_obj.x == 3 | ||
self.js_obj.y = 3 | ||
assert isinstance(self.js_obj['y'], float) | ||
assert self.js_obj['y'] == 3 | ||
|
||
def test_str(self): | ||
assert str(self.js_obj) == 'JSObject()' | ||
|
||
def test_call(self): | ||
assert self.func(3, 4) == 7 | ||
assert self.func('a', 'bc') == 'abc' | ||
|
||
def test_obj_wrap_unwrap(self): | ||
self.js_obj['x'] = self.js_obj_2 | ||
assert str(self.js_obj.x) == 'JSObject()' | ||
|
||
def test_floats(self): | ||
self.js_obj.y = 3.5 | ||
assert self.js_obj.y == 3.5 | ||
|
||
def test_bools(self): | ||
self.js_obj.x = True | ||
assert self.js_obj.x | ||
|
||
def test_none(self): | ||
self.js_obj.x = None | ||
|
||
def test_property_list(self): | ||
x = self.interpret(''' | ||
function c () { | ||
this.x = 1 | ||
this.y = 2 | ||
this.z = 3 | ||
} | ||
new c() | ||
''') | ||
assert x.__dict__ == {'x':1, 'y':2, 'z':3} | ||
|
||
def test_global(self): | ||
self.interpret(''' | ||
xxx = 3 | ||
''') | ||
assert self.globals.xxx == 3 | ||
|
||
def test_method(self): | ||
x = self.interpret(''' | ||
function c () { | ||
this.zzz = 3; | ||
this.f = function (x) { | ||
return (this.zzz + x); | ||
}; | ||
} | ||
new c() | ||
''') | ||
assert x.f(3) == 6 | ||
|
||
def test_raising_call(self): | ||
f = self.interpret(''' | ||
function f(x) { | ||
throw TypeError; | ||
} | ||
f | ||
''') | ||
raises(self.JSException, f, 3) | ||
|
||
# XXX more raising tests | ||
|
||
def test_wrapped_callback(self): | ||
f = self.interpret(''' | ||
function f(x) { | ||
return x(3); | ||
} | ||
f | ||
''') | ||
res = f(lambda x: x + 3) | ||
assert res == 3 + 3 |
Oops, something went wrong.