pythonista-jscore-runtime 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
jscore_runtime.py
ADDED
|
@@ -0,0 +1,2448 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pythonista JSCore Runtime Framework - Execute JavaScript and WebAssembly with seamless interop support natively in Pythonista 3.
|
|
3
|
+
Develop apps with Python, JavaScript and WebAssembly libraries, components and code.
|
|
4
|
+
|
|
5
|
+
https://github.com/M4nw3l/pythonista-jscore-runtime
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = '0.0.1'
|
|
9
|
+
|
|
10
|
+
from ctypes import *
|
|
11
|
+
from ctypes.util import find_library
|
|
12
|
+
from objc_util import *
|
|
13
|
+
from objc_util import (c, object_getClass, class_getName, objc_getProtocol)
|
|
14
|
+
import weakref
|
|
15
|
+
from datetime import (datetime, timezone)
|
|
16
|
+
import json, re
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
import tempfile, shutil, os
|
|
19
|
+
import ui
|
|
20
|
+
|
|
21
|
+
NSDate = ObjCClass("NSDate")
|
|
22
|
+
NSFileManager = ObjCClass("NSFileManager")
|
|
23
|
+
|
|
24
|
+
#objective c helpers
|
|
25
|
+
class objc:
|
|
26
|
+
# load_library from rubicon
|
|
27
|
+
#https://github.com/beeware/rubicon-objc/blob/1a97f483fdd83f4fc31050ee863535e3ed962944/src/rubicon/objc/runtime.py#L77
|
|
28
|
+
_lib_path = ["/usr/lib"]
|
|
29
|
+
_framework_path = ["/System/Library/Frameworks"]
|
|
30
|
+
@staticmethod
|
|
31
|
+
def load_library(name):
|
|
32
|
+
path = find_library(name)
|
|
33
|
+
if path is not None:
|
|
34
|
+
return CDLL(path)
|
|
35
|
+
|
|
36
|
+
for loc in _lib_path:
|
|
37
|
+
try:
|
|
38
|
+
return CDLL(os.path.join(loc, "lib" + name + ".dylib"))
|
|
39
|
+
except OSError:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
for loc in _framework_path:
|
|
43
|
+
try:
|
|
44
|
+
return CDLL(os.path.join(loc, name + ".framework", name))
|
|
45
|
+
except OSError:
|
|
46
|
+
pass
|
|
47
|
+
raise ValueError(f"Library {name!r} not found")
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def const(dll, name, typ = c_void_p):
|
|
51
|
+
if issubclass(typ, c_void_p):
|
|
52
|
+
return ObjCInstance(typ.in_dll(dll, name))
|
|
53
|
+
return typ.in_dll(dll, name)
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def c_func(func, restype, *argtypes):
|
|
57
|
+
func.restype = restype
|
|
58
|
+
func.argtypes = argtypes
|
|
59
|
+
return staticmethod(func)
|
|
60
|
+
|
|
61
|
+
objc_allocateProtocol = c_func(c.objc_allocateProtocol, c_void_p, c_char_p)
|
|
62
|
+
objc_protocol_addMethodDescription = c_func(c.protocol_addMethodDescription, None, c_void_p, c_void_p, c_char_p, c_bool, c_bool)
|
|
63
|
+
objc_protocol_addProtocol = c_func(c.protocol_addProtocol, None, c_void_p, c_void_p)
|
|
64
|
+
objc_protocol_addProperty = c_func(c.protocol_addProperty, None, c_void_p, c_char_p, c_void_p, c_uint, c_bool, c_bool)
|
|
65
|
+
objc_registerProtocol = c_func(c.objc_registerProtocol, None, c_void_p)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def getProtocol(name):
|
|
69
|
+
return objc_getProtocol(name.encode("ascii"))
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def allocateProtocol(name):
|
|
73
|
+
return objc.objc_allocateProtocol(name.encode("ascii"))
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def get_type_encoding(typ):
|
|
77
|
+
# https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
|
|
78
|
+
# objective-c type encoding from a source type reference string - covers a fair few cases but is incomplete.
|
|
79
|
+
# While it also cannot understand some c type definitions properly e.g definitions containing refernces to typedef names
|
|
80
|
+
enc = []
|
|
81
|
+
while typ.endswith('*'):
|
|
82
|
+
enc.append("^")
|
|
83
|
+
typ = typ[:len(typ)-1]
|
|
84
|
+
typ = typ.strip()
|
|
85
|
+
parts = typ.split(' ')
|
|
86
|
+
upper = False
|
|
87
|
+
val = None
|
|
88
|
+
while len(parts) > 0:
|
|
89
|
+
part = parts.pop(0)
|
|
90
|
+
if part == "unsigned":
|
|
91
|
+
upper = True
|
|
92
|
+
elif part == "char":
|
|
93
|
+
c = len(enc)
|
|
94
|
+
for i in range(c):
|
|
95
|
+
enc[i] = "*" if enc[i] == "^" else enc[i]
|
|
96
|
+
val = "c" if c == 0 else ""
|
|
97
|
+
elif part in ["int", "void", "short", "float", "double"]:
|
|
98
|
+
val = part[0]
|
|
99
|
+
elif part == "long":
|
|
100
|
+
val = "l" if val is None else "q"
|
|
101
|
+
elif part in ["bool", "BOOL", "_Bool"]:
|
|
102
|
+
val = "B"
|
|
103
|
+
array = typ.endswith("]")
|
|
104
|
+
ptr = len(enc) == 1
|
|
105
|
+
struct = val is None and len(enc) > 0
|
|
106
|
+
arrlen=""
|
|
107
|
+
if array:
|
|
108
|
+
idx = list(typ).index('[')
|
|
109
|
+
arrlen = typ[idx:len(typ)-1]
|
|
110
|
+
typ = typ[:idx]
|
|
111
|
+
if val is not None:
|
|
112
|
+
enc.append(val)
|
|
113
|
+
if struct:
|
|
114
|
+
enc.append("{")
|
|
115
|
+
enc.append(typ)
|
|
116
|
+
if ptr:
|
|
117
|
+
enc.append("=#}")
|
|
118
|
+
else:
|
|
119
|
+
enc.append("}")
|
|
120
|
+
if array:
|
|
121
|
+
enc.insert(0, "["+arrlen)
|
|
122
|
+
enc.append("]")
|
|
123
|
+
return "".join(enc)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def protocol_addMethodDescription(protocol, method, required, types = None, instance = None):
|
|
127
|
+
# add a protocol method description from its objective-c definition
|
|
128
|
+
method = method.strip()
|
|
129
|
+
name = "".join(re.findall("([A-z0-9]+:)", method))
|
|
130
|
+
description = ""
|
|
131
|
+
if instance is None:
|
|
132
|
+
instance = method.startswith("-")
|
|
133
|
+
if not instance and not method.startswith("+"):
|
|
134
|
+
raise ValueError(f"Method type is not specified as class (+) or instance (-) for method '{method}'")
|
|
135
|
+
if types is not None:
|
|
136
|
+
if isinstance(types, str):
|
|
137
|
+
description = types
|
|
138
|
+
else:
|
|
139
|
+
description = "".join(types)
|
|
140
|
+
else:
|
|
141
|
+
types = re.findall("\\(([A-z0-9 \*_\[\]]+)\\)", method)
|
|
142
|
+
description = []
|
|
143
|
+
for typ in types:
|
|
144
|
+
enc = objc.get_type_encoding(typ)
|
|
145
|
+
description.append(enc)
|
|
146
|
+
description = "".join(description)
|
|
147
|
+
#print(name)
|
|
148
|
+
selector = sel(name)
|
|
149
|
+
description = description.encode("ascii")
|
|
150
|
+
objc.objc_protocol_addMethodDescription(protocol, selector, description, required, instance)
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def protocol_addProperty(protocol, property, required, types = None, instance = None):
|
|
154
|
+
raise NotImplementedError("TODO: protocol_addProperty")
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def protocol_addProtocol(protocol, parent):
|
|
158
|
+
objc.objc_protocol_addProtocol(protocol, parent)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
# create a protocol from an objc definition
|
|
162
|
+
def protocol(name, body = [], types = [], protocols=["NSObject"], debug = True):
|
|
163
|
+
basename = name
|
|
164
|
+
p = objc.getProtocol(name)
|
|
165
|
+
if p is not None and not debug:
|
|
166
|
+
return name
|
|
167
|
+
counter = 0
|
|
168
|
+
while debug:
|
|
169
|
+
p = objc.getProtocol(name)
|
|
170
|
+
if p is None:
|
|
171
|
+
break
|
|
172
|
+
name = f"{basename}_{counter}"
|
|
173
|
+
counter = counter + 1
|
|
174
|
+
p = objc.allocateProtocol(name)
|
|
175
|
+
for id in protocols:
|
|
176
|
+
parent = objc.getProtocol("NSObject")
|
|
177
|
+
if parent is None:
|
|
178
|
+
raise ValueError(f"Protocol not found '{id}'")
|
|
179
|
+
objc.protocol_addProtocol(p, parent)
|
|
180
|
+
required = True
|
|
181
|
+
typesLen = len(types)
|
|
182
|
+
for i in range(len(body)):
|
|
183
|
+
method = body[i].strip()
|
|
184
|
+
methodTypes = None
|
|
185
|
+
if i < typesLen:
|
|
186
|
+
t = types[i]
|
|
187
|
+
if isinstance(t, str):
|
|
188
|
+
t = t.strip()
|
|
189
|
+
if t != "":
|
|
190
|
+
methodTypes = t
|
|
191
|
+
elif t is not None and len(t) > 0:
|
|
192
|
+
methodTypes = t
|
|
193
|
+
if method == "@required":
|
|
194
|
+
required = True
|
|
195
|
+
elif method == "@optional":
|
|
196
|
+
required = False
|
|
197
|
+
elif not ":" in method:
|
|
198
|
+
objc.protocol_addProperty(p, method, required, methodTypes) # TODO: properties
|
|
199
|
+
print(method)
|
|
200
|
+
else:
|
|
201
|
+
objc.protocol_addMethodDescription(p, method, required, methodTypes)
|
|
202
|
+
objc.objc_registerProtocol(p)
|
|
203
|
+
#print(name)
|
|
204
|
+
return name
|
|
205
|
+
|
|
206
|
+
new_class = create_objc_class
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def ns_class(nsobject):
|
|
210
|
+
if not (isinstance(nsobject, c_void_p) or isinstance(nsobject, ObjCInstance)):
|
|
211
|
+
return None
|
|
212
|
+
objClass = ObjCInstance(object_getClass(nsobject))
|
|
213
|
+
objClassName = class_getName(objClass)
|
|
214
|
+
return objClass
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def ns_subclass_of(nsobject, objcClass, objClass=None):
|
|
218
|
+
if not (isinstance(nsobject, c_void_p) or isinstance(nsobject, ObjCInstance)):
|
|
219
|
+
return False
|
|
220
|
+
if objClass is None:
|
|
221
|
+
objClass = objc.ns_class(nsobject)
|
|
222
|
+
if objClass is None:
|
|
223
|
+
return False
|
|
224
|
+
return objClass.isSubclassOfClass_(objcClass)
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
def ns_to_py(nsobject, objClass=None):
|
|
228
|
+
if objClass is None:
|
|
229
|
+
objClass = objc.ns_class(nsobject)
|
|
230
|
+
if objc.ns_subclass_of(nsobject, NSString, objClass):
|
|
231
|
+
v = str(nsobject)
|
|
232
|
+
return v
|
|
233
|
+
if objc.ns_subclass_of(nsobject, NSNumber, objClass):
|
|
234
|
+
nsnumber = nsobject
|
|
235
|
+
doubleValue = float(nsnumber.doubleValue())
|
|
236
|
+
intValue = int(nsnumber.longLongValue())
|
|
237
|
+
if doubleValue == intValue:
|
|
238
|
+
return intValue
|
|
239
|
+
return doubleValue
|
|
240
|
+
if objc.ns_subclass_of(nsobject, NSDate, objClass):
|
|
241
|
+
nsdate = nsobject
|
|
242
|
+
timestamp = nsdate.timeIntervalSince1970()
|
|
243
|
+
return datetime.fromtimestamp(timestamp, timezone.utc)
|
|
244
|
+
if objc.ns_subclass_of(nsobject, NSArray, objClass):
|
|
245
|
+
nsarray = nsobject
|
|
246
|
+
items = []
|
|
247
|
+
for i in range(nsarray.count()):
|
|
248
|
+
item = objc.ns_to_py(nsarray.objectAtIndex_(i))
|
|
249
|
+
items.append(item)
|
|
250
|
+
return items
|
|
251
|
+
if objc.ns_subclass_of(nsobject, NSDictionary, objClass):
|
|
252
|
+
nsdict = nsobject
|
|
253
|
+
keys = nsdict.allKeys()
|
|
254
|
+
values = nsdict.allValues()
|
|
255
|
+
items = {}
|
|
256
|
+
for i in range(nsdict.count()):
|
|
257
|
+
key = objc.ns_to_py(keys.objectAtIndex_(i))
|
|
258
|
+
value = objc.ns_to_py(values.objectAtIndex_(i))
|
|
259
|
+
items[key] = value
|
|
260
|
+
return items
|
|
261
|
+
className = "unknown"
|
|
262
|
+
if objClass is not None:
|
|
263
|
+
className = class_getName(objClass)
|
|
264
|
+
raise NotImplementedError("Unhandled NSObject type {objClass} ({className}) for {nsobject}.")
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def c_array(count, items = None, typ = c_byte, ptr = c_void_p):
|
|
268
|
+
if items is None:
|
|
269
|
+
if callable(count):
|
|
270
|
+
items = []
|
|
271
|
+
iter = count
|
|
272
|
+
count = 0
|
|
273
|
+
while True:
|
|
274
|
+
try:
|
|
275
|
+
item = iter(count)
|
|
276
|
+
if item is None:
|
|
277
|
+
break
|
|
278
|
+
count = count + 1
|
|
279
|
+
items.append(item)
|
|
280
|
+
except:
|
|
281
|
+
break
|
|
282
|
+
elif isinstance(count, bytes) or isinstance(count, list):
|
|
283
|
+
items = count
|
|
284
|
+
count = len(items)
|
|
285
|
+
if count == 0:
|
|
286
|
+
if ptr is None:
|
|
287
|
+
return None
|
|
288
|
+
return cast(c_void_p(None), ptr) # NULL
|
|
289
|
+
c_array_typ = typ * count
|
|
290
|
+
array = c_array_typ()
|
|
291
|
+
if items is None:
|
|
292
|
+
if ptr is None:
|
|
293
|
+
return array
|
|
294
|
+
return cast(array, ptr)
|
|
295
|
+
if isinstance(items, bytes) or isinstance(items, list):
|
|
296
|
+
for i in range(count):
|
|
297
|
+
array[i] = items[i]
|
|
298
|
+
elif callable(items):
|
|
299
|
+
for i in range(count):
|
|
300
|
+
array[i] = items(i)
|
|
301
|
+
else:
|
|
302
|
+
raise NotImplementedError()
|
|
303
|
+
if ptr is None:
|
|
304
|
+
return array
|
|
305
|
+
return cast(array, ptr)
|
|
306
|
+
|
|
307
|
+
@staticmethod
|
|
308
|
+
def c_array_p(count, items = None, typ = c_void_p, ptr = c_void_p):
|
|
309
|
+
return objc.c_array(count, items, typ, ptr)
|
|
310
|
+
|
|
311
|
+
@staticmethod
|
|
312
|
+
def nsdata_from_file(path, fileManager = None):
|
|
313
|
+
if fileManager is None:
|
|
314
|
+
fileManager = NSFileManager.defaultManager()
|
|
315
|
+
path = Path(str(path))
|
|
316
|
+
if not path.is_absolute():
|
|
317
|
+
path = path.cwd().joinpath(path)
|
|
318
|
+
if not path.exists():
|
|
319
|
+
raise FileNotFoundError(f"File not found at path '{path}'")
|
|
320
|
+
path = str(path)
|
|
321
|
+
data = fileManager.contentsAtPath_(path)
|
|
322
|
+
return data
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
#JavaScriptCore api
|
|
326
|
+
class jscore:
|
|
327
|
+
JSVirtualMachine = ObjCClass("JSVirtualMachine")
|
|
328
|
+
JSContext = ObjCClass("JSContext")
|
|
329
|
+
JSValue = ObjCClass("JSValue")
|
|
330
|
+
JSManagedValue = ObjCClass("JSManagedValue")
|
|
331
|
+
JSScript = ObjCClass("JSScript")
|
|
332
|
+
JSExport = ObjCClass("JSExport")
|
|
333
|
+
JSObjCClassInfo = ObjCClass("JSObjCClassInfo")
|
|
334
|
+
JSWrapperMap = ObjCClass("JSWrapperMap")
|
|
335
|
+
JSVMWrapperCache = ObjCClass("JSVMWrapperCache")
|
|
336
|
+
WTFWebFileManagerDelegate = ObjCClass("WTFWebFileManagerDelegate")
|
|
337
|
+
|
|
338
|
+
# c api
|
|
339
|
+
lib = objc.load_library("JavaScriptCore")
|
|
340
|
+
|
|
341
|
+
JSContextGetGroup = objc.c_func(lib.JSContextGetGroup, c_void_p, c_void_p)
|
|
342
|
+
JSContextGroupCreate = objc.c_func(lib.JSContextGroupCreate, c_void_p)
|
|
343
|
+
JSContextGroupRetain = objc.c_func(lib.JSContextGroupRetain, c_void_p, c_void_p)
|
|
344
|
+
JSContextGroupRelease = objc.c_func(lib.JSContextGroupRelease, None, c_void_p)
|
|
345
|
+
JSContextGetGlobalContext = objc.c_func(lib.JSContextGetGlobalContext, c_void_p, c_void_p)
|
|
346
|
+
JSContextGetGlobalObject = objc.c_func(lib.JSContextGetGlobalObject, c_void_p, c_void_p)
|
|
347
|
+
|
|
348
|
+
JSGlobalContextCreate = objc.c_func(lib.JSGlobalContextCreate, c_void_p, c_void_p)
|
|
349
|
+
JSGlobalContextCreateInGroup = objc.c_func(lib.JSGlobalContextCreateInGroup, c_void_p, c_void_p, c_void_p)
|
|
350
|
+
JSGlobalContextRetain = objc.c_func(lib.JSGlobalContextRetain, c_void_p, c_void_p)
|
|
351
|
+
JSGlobalContextRelease = objc.c_func(lib.JSGlobalContextRelease, None, c_void_p)
|
|
352
|
+
JSGlobalContextCopyName = objc.c_func(lib.JSGlobalContextCopyName, c_void_p, c_void_p)
|
|
353
|
+
JSGlobalContextSetName = objc.c_func(lib.JSGlobalContextSetName, None, c_void_p, c_void_p)
|
|
354
|
+
JSGlobalContextIsInspectable = objc.c_func(lib.JSGlobalContextIsInspectable, c_bool, c_void_p)
|
|
355
|
+
JSGlobalContextSetInspectable = objc.c_func(lib.JSGlobalContextSetInspectable, None, c_void_p, c_bool)
|
|
356
|
+
|
|
357
|
+
JSChar_p = POINTER(c_ushort)
|
|
358
|
+
JSStringCreateWithCharacters = objc.c_func(lib.JSStringCreateWithCharacters, c_void_p, c_void_p, c_size_t)
|
|
359
|
+
JSStringCreateWithUTF8CString = objc.c_func(lib.JSStringCreateWithUTF8CString, c_void_p, c_void_p)
|
|
360
|
+
JSStringRetain = objc.c_func(lib.JSStringRetain, c_void_p, c_void_p)
|
|
361
|
+
JSStringRelease = objc.c_func(lib.JSStringRelease, None, c_void_p)
|
|
362
|
+
JSStringGetLength = objc.c_func(lib.JSStringGetLength, c_size_t, c_void_p)
|
|
363
|
+
JSStringGetCharactersPtr = objc.c_func(lib.JSStringGetCharactersPtr, JSChar_p, c_void_p)
|
|
364
|
+
JSStringGetMaximumUTF8CStringSize = objc.c_func(lib.JSStringGetMaximumUTF8CStringSize, c_size_t, c_void_p)
|
|
365
|
+
JSStringGetUTF8CString = objc.c_func(lib.JSStringGetUTF8CString, c_size_t, c_void_p, c_void_p, c_size_t)
|
|
366
|
+
JSStringIsEqual = objc.c_func(lib.JSStringIsEqual, c_bool, c_void_p, c_void_p)
|
|
367
|
+
JSStringIsEqualToUTF8CString = objc.c_func(lib.JSStringIsEqualToUTF8CString, c_bool, c_void_p, c_void_p)
|
|
368
|
+
JSStringCreateWithCFString = objc.c_func(lib.JSStringCreateWithCFString, c_void_p, c_void_p)
|
|
369
|
+
JSStringCopyCFString = objc.c_func(lib.JSStringCopyCFString, c_void_p, c_void_p, c_void_p)
|
|
370
|
+
|
|
371
|
+
JSValueGetType = objc.c_func(lib.JSValueGetType, c_int, c_void_p, c_void_p)
|
|
372
|
+
JSValueIsUndefined = objc.c_func(lib.JSValueIsUndefined, c_bool, c_void_p, c_void_p)
|
|
373
|
+
JSValueIsNull = objc.c_func(lib.JSValueIsNull, c_bool, c_void_p, c_void_p)
|
|
374
|
+
JSValueIsBoolean = objc.c_func(lib.JSValueIsBoolean, c_bool, c_void_p, c_void_p)
|
|
375
|
+
JSValueIsNumber = objc.c_func(lib.JSValueIsNumber, c_bool, c_void_p, c_void_p)
|
|
376
|
+
JSValueIsString = objc.c_func(lib.JSValueIsString, c_bool, c_void_p, c_void_p)
|
|
377
|
+
JSValueIsSymbol = objc.c_func(lib.JSValueIsSymbol, c_bool, c_void_p, c_void_p)
|
|
378
|
+
JSValueIsObject = objc.c_func(lib.JSValueIsObject, c_bool, c_void_p, c_void_p)
|
|
379
|
+
JSValueIsObjectOfClass = objc.c_func(lib.JSValueIsObjectOfClass, c_bool, c_void_p, c_void_p, c_void_p)
|
|
380
|
+
JSValueIsArray = objc.c_func(lib.JSValueIsArray, c_bool, c_void_p, c_void_p)
|
|
381
|
+
JSValueIsDate = objc.c_func(lib.JSValueIsDate, c_bool, c_void_p, c_void_p)
|
|
382
|
+
JSValueGetTypedArrayType = objc.c_func(lib.JSValueGetTypedArrayType, c_int, c_void_p, c_void_p, c_void_p)
|
|
383
|
+
JSValueMakeUndefined = objc.c_func(lib.JSValueMakeUndefined, c_void_p, c_void_p)
|
|
384
|
+
JSValueMakeNull = objc.c_func(lib.JSValueMakeNull, c_void_p, c_void_p)
|
|
385
|
+
JSValueMakeBoolean = objc.c_func(lib.JSValueMakeBoolean, c_void_p, c_void_p, c_bool)
|
|
386
|
+
JSValueMakeNumber = objc.c_func(lib.JSValueMakeNumber, c_void_p, c_void_p, c_double)
|
|
387
|
+
JSValueMakeString = objc.c_func(lib.JSValueMakeString, c_void_p, c_void_p, c_void_p)
|
|
388
|
+
JSValueMakeSymbol = objc.c_func(lib.JSValueMakeSymbol, c_void_p, c_void_p, c_void_p)
|
|
389
|
+
JSValueToBoolean = objc.c_func(lib.JSValueToBoolean, c_bool, c_void_p, c_void_p)
|
|
390
|
+
JSValueToNumber = objc.c_func(lib.JSValueToNumber, c_double, c_void_p, c_void_p, c_void_p)
|
|
391
|
+
JSValueToStringCopy = objc.c_func(lib.JSValueToStringCopy, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
392
|
+
JSValueToObject = objc.c_func(lib.JSValueToObject, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
393
|
+
JSValueMakeFromJSONString = objc.c_func(lib.JSValueMakeFromJSONString, c_void_p, c_void_p, c_void_p)
|
|
394
|
+
JSValueCreateJSONString = objc.c_func(lib.JSValueCreateJSONString, c_void_p, c_void_p, c_void_p, c_uint, c_void_p)
|
|
395
|
+
JSValueIsEqual = objc.c_func(lib.JSValueIsEqual, c_bool, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
396
|
+
JSValueIsStrictEqual = objc.c_func(lib.JSValueIsStrictEqual, c_bool, c_void_p, c_void_p, c_void_p)
|
|
397
|
+
JSValueIsInstanceOfConstructor = objc.c_func(lib.JSValueIsInstanceOfConstructor, c_bool, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
398
|
+
JSValueProtect = objc.c_func(lib.JSValueProtect, None, c_void_p, c_void_p)
|
|
399
|
+
JSValueUnprotect = objc.c_func(lib.JSValueUnprotect, None, c_void_p, c_void_p)
|
|
400
|
+
|
|
401
|
+
JSObjectCallAsConstructor = objc.c_func(lib.JSObjectCallAsConstructor, c_void_p, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
402
|
+
JSObjectCallAsFunction = objc.c_func(lib.JSObjectCallAsFunction, c_void_p, c_void_p, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
403
|
+
JSObjectCopyPropertyNames = objc.c_func(lib.JSObjectCopyPropertyNames, c_void_p, c_void_p, c_void_p)
|
|
404
|
+
JSObjectDeleteProperty = objc.c_func(lib.JSObjectDeleteProperty, c_bool, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
405
|
+
JSObjectGetPrivate = objc.c_func(lib.JSObjectGetPrivate, c_void_p, c_void_p)
|
|
406
|
+
JSObjectGetProperty = objc.c_func(lib.JSObjectGetProperty, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
407
|
+
JSObjectGetPropertyAtIndex = objc.c_func(lib.JSObjectGetPropertyAtIndex, c_void_p, c_void_p, c_void_p, c_uint, c_void_p)
|
|
408
|
+
JSObjectGetPrototype = objc.c_func(lib.JSObjectGetPrototype, c_void_p, c_void_p, c_void_p)
|
|
409
|
+
JSObjectHasProperty = objc.c_func(lib.JSObjectHasProperty, c_bool, c_void_p, c_void_p, c_void_p)
|
|
410
|
+
JSObjectIsConstructor = objc.c_func(lib.JSObjectIsConstructor, c_bool, c_void_p, c_void_p)
|
|
411
|
+
JSObjectIsFunction = objc.c_func(lib.JSObjectIsFunction, c_bool, c_void_p, c_void_p)
|
|
412
|
+
JSObjectMake = objc.c_func(lib.JSObjectMake, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
413
|
+
JSObjectMakeArray = objc.c_func(lib.JSObjectMakeArray, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
414
|
+
JSObjectMakeConstructor = objc.c_func(lib.JSObjectMakeConstructor, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
415
|
+
JSObjectMakeDate = objc.c_func(lib.JSObjectMakeDate, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
416
|
+
JSObjectMakeError = objc.c_func(lib.JSObjectMakeError, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
417
|
+
JSObjectMakeFunction = objc.c_func(lib.JSObjectMakeFunction, c_void_p, c_void_p, c_void_p, c_uint, c_void_p, c_void_p, c_void_p, c_int, c_void_p)
|
|
418
|
+
JSObjectCallAsFunctionCallback = CFUNCTYPE(c_void_p, c_void_p, c_void_p, c_void_p, c_ulong, c_void_p, c_void_p)
|
|
419
|
+
JSObjectMakeFunctionWithCallback = objc.c_func(lib.JSObjectMakeFunctionWithCallback, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
420
|
+
JSObjectMakeRegExp = objc.c_func(lib.JSObjectMakeRegExp, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
421
|
+
JSObjectSetPrivate = objc.c_func(lib.JSObjectSetPrivate, c_bool, c_void_p, c_void_p)
|
|
422
|
+
JSObjectSetProperty = objc.c_func(lib.JSObjectSetProperty, None, c_void_p, c_void_p, c_void_p, c_void_p, c_uint, c_void_p)
|
|
423
|
+
JSObjectSetPropertyAtIndex = objc.c_func(lib.JSObjectSetPropertyAtIndex, None, c_void_p, c_void_p, c_uint, c_void_p, c_void_p)
|
|
424
|
+
JSObjectGetPropertyForKey = objc.c_func(lib.JSObjectGetPropertyForKey, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
425
|
+
JSObjectSetPrototype = objc.c_func(lib.JSObjectSetPrototype, None, c_void_p, c_void_p, c_void_p)
|
|
426
|
+
JSObjectDeletePropertyForKey = objc.c_func(lib.JSObjectDeletePropertyForKey, c_bool, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
427
|
+
JSObjectHasPropertyForKey = objc.c_func(lib.JSObjectHasPropertyForKey, c_bool, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
428
|
+
JSObjectSetPropertyForKey = objc.c_func(lib.JSObjectSetPropertyForKey, None, c_void_p, c_void_p, c_void_p, c_void_p, c_uint, c_void_p)
|
|
429
|
+
JSObjectMakeDeferredPromise = objc.c_func(lib.JSObjectMakeDeferredPromise, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
430
|
+
|
|
431
|
+
JSClassCreate = objc.c_func(lib.JSClassCreate, c_void_p, c_void_p)
|
|
432
|
+
JSClassRelease = objc.c_func(lib.JSClassRelease, None, c_void_p)
|
|
433
|
+
JSClassRetain = objc.c_func(lib.JSClassRetain, c_void_p, c_void_p)
|
|
434
|
+
|
|
435
|
+
JSPropertyNameAccumulatorAddName = objc.c_func(lib.JSPropertyNameAccumulatorAddName, None, c_void_p, c_void_p)
|
|
436
|
+
JSPropertyNameArrayGetCount = objc.c_func(lib.JSPropertyNameArrayGetCount, c_size_t, c_void_p)
|
|
437
|
+
JSPropertyNameArrayGetNameAtIndex = objc.c_func(lib.JSPropertyNameArrayGetNameAtIndex, c_void_p, c_void_p, c_size_t)
|
|
438
|
+
JSPropertyNameArrayRelease = objc.c_func(lib.JSPropertyNameArrayRelease, None, c_void_p)
|
|
439
|
+
JSPropertyNameArrayRetain = objc.c_func(lib.JSPropertyNameArrayRetain, c_void_p, c_void_p)
|
|
440
|
+
|
|
441
|
+
JSObjectMakeTypedArray = objc.c_func(lib.JSObjectMakeTypedArray, c_void_p, c_void_p, c_int, c_size_t, c_void_p)
|
|
442
|
+
JSObjectMakeTypedArrayWithBytesNoCopy = objc.c_func(lib.JSObjectMakeTypedArrayWithBytesNoCopy, c_void_p, c_void_p, c_int, c_void_p, c_size_t, c_void_p, c_void_p, c_void_p)
|
|
443
|
+
JSObjectMakeTypedArrayWithArrayBuffer = objc.c_func(lib.JSObjectMakeTypedArrayWithArrayBuffer, c_void_p, c_void_p, c_int, c_void_p, c_void_p)
|
|
444
|
+
JSObjectMakeTypedArrayWithArrayBufferAndOffset = objc.c_func(lib.JSObjectMakeTypedArrayWithArrayBufferAndOffset, c_void_p, c_void_p, c_int, c_void_p, c_size_t, c_size_t, c_void_p)
|
|
445
|
+
JSObjectGetTypedArrayBytesPtr = objc.c_func(lib.JSObjectGetTypedArrayBytesPtr, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
446
|
+
JSObjectGetTypedArrayLength = objc.c_func(lib.JSObjectGetTypedArrayLength, c_size_t, c_void_p, c_void_p, c_void_p)
|
|
447
|
+
JSObjectGetTypedArrayByteLength = objc.c_func(lib.JSObjectGetTypedArrayByteLength, c_size_t, c_void_p, c_void_p, c_void_p)
|
|
448
|
+
JSObjectGetTypedArrayByteOffset = objc.c_func(lib.JSObjectGetTypedArrayByteOffset, c_size_t, c_void_p, c_void_p, c_void_p)
|
|
449
|
+
JSObjectGetTypedArrayBuffer = objc.c_func(lib.JSObjectGetTypedArrayBuffer, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
450
|
+
JSObjectMakeArrayBufferWithBytesNoCopy = objc.c_func(lib.JSObjectMakeArrayBufferWithBytesNoCopy, c_void_p, c_void_p, c_void_p, c_size_t, c_void_p, c_void_p, c_void_p)
|
|
451
|
+
JSObjectGetArrayBufferByteLength = objc.c_func(lib.JSObjectGetArrayBufferByteLength, c_size_t, c_void_p, c_void_p, c_void_p)
|
|
452
|
+
JSObjectGetArrayBufferBytesPtr = objc.c_func(lib.JSObjectGetArrayBufferBytesPtr, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
453
|
+
|
|
454
|
+
kJSTypedArrayTypeInt8Array = 0
|
|
455
|
+
kJSTypedArrayTypeInt16Array = 1
|
|
456
|
+
kJSTypedArrayTypeInt32Array = 2
|
|
457
|
+
kJSTypedArrayTypeUint8Array = 3
|
|
458
|
+
kJSTypedArrayTypeUint8ClampedArray = 4
|
|
459
|
+
kJSTypedArrayTypeUint16Array = 5
|
|
460
|
+
kJSTypedArrayTypeUint32Array = 6
|
|
461
|
+
kJSTypedArrayTypeFloat32Array = 7
|
|
462
|
+
kJSTypedArrayTypeFloat64Array = 8
|
|
463
|
+
kJSTypedArrayTypeArrayBuffer = 9
|
|
464
|
+
kJSTypedArrayTypeNone = 10
|
|
465
|
+
kJSTypedArrayTypeBigInt64Array = 11
|
|
466
|
+
kJSTypedArrayTypeBigUint64Array = 12
|
|
467
|
+
|
|
468
|
+
JSCheckScriptSyntax = objc.c_func(lib.JSCheckScriptSyntax, c_bool, c_void_p, c_void_p, c_void_p, c_int, c_void_p)
|
|
469
|
+
JSEvaluateScript = objc.c_func(lib.JSEvaluateScript, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p, c_int, c_void_p)
|
|
470
|
+
JSGarbageCollect = objc.c_func(lib.JSGarbageCollect, None, c_void_p)
|
|
471
|
+
|
|
472
|
+
JSBigIntCreateWithDouble = objc.c_func(lib.JSBigIntCreateWithDouble, c_void_p, c_void_p, c_double, c_void_p)
|
|
473
|
+
JSBigIntCreateWithInt64 = objc.c_func(lib.JSBigIntCreateWithInt64, c_void_p, c_void_p, c_int64, c_void_p)
|
|
474
|
+
JSBigIntCreateWithString = objc.c_func(lib.JSBigIntCreateWithString, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
475
|
+
JSBigIntCreateWithUInt64 = objc.c_func(lib.JSBigIntCreateWithUInt64, c_void_p, c_void_p, c_uint64, c_void_p)
|
|
476
|
+
|
|
477
|
+
JSValueCompare = objc.c_func(lib.JSValueCompare, c_int, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
478
|
+
JSValueCompareDouble = objc.c_func(lib.JSValueCompareDouble, c_int, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
479
|
+
JSValueCompareInt64 = objc.c_func(lib.JSValueCompareInt64, c_int, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
480
|
+
JSValueCompareUInt64 = objc.c_func(lib.JSValueCompareUInt64, c_int, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
481
|
+
JSValueIsBigInt = objc.c_func(lib.JSValueIsBigInt, c_bool, c_void_p, c_void_p)
|
|
482
|
+
JSValueToInt32 = objc.c_func(lib.JSValueToInt32, c_int32, c_void_p, c_void_p, c_void_p)
|
|
483
|
+
JSValueToInt64 = objc.c_func(lib.JSValueToInt64, c_int64, c_void_p, c_void_p, c_void_p)
|
|
484
|
+
JSValueToUInt32 = objc.c_func(lib.JSValueToUInt32, c_uint32, c_void_p, c_void_p, c_void_p)
|
|
485
|
+
JSValueToUInt64 = objc.c_func(lib.JSValueToUInt64, c_uint64, c_void_p, c_void_p, c_void_p)
|
|
486
|
+
|
|
487
|
+
# internal api (for module loader)
|
|
488
|
+
|
|
489
|
+
# https://github.com/WebKit/WebKit/blob/7321e0dc891bcc0dce916e8d71204e58d92641cd/Source/JavaScriptCore/API/JSScriptRefPrivate.h
|
|
490
|
+
#JSScriptRef
|
|
491
|
+
JSScriptCreateReferencingImmortalASCIIText = objc.c_func(lib.JSScriptCreateReferencingImmortalASCIIText, c_void_p, c_void_p, c_void_p, c_int, c_void_p, c_size_t, c_void_p, c_void_p)
|
|
492
|
+
JSScriptCreateFromString = objc.c_func(lib.JSScriptCreateFromString, c_void_p, c_void_p, c_void_p, c_int, c_void_p, c_void_p, c_void_p)
|
|
493
|
+
JSScriptRetain = objc.c_func(lib.JSScriptRetain, None, c_void_p)
|
|
494
|
+
JSScriptRelease = objc.c_func(lib.JSScriptRelease, None, c_void_p)
|
|
495
|
+
JSScriptEvaluate = objc.c_func(lib.JSScriptEvaluate, c_void_p, c_void_p, c_void_p, c_void_p, c_void_p)
|
|
496
|
+
|
|
497
|
+
kJSScriptTypeProgram = 0
|
|
498
|
+
kJSScriptTypeModule = 1
|
|
499
|
+
|
|
500
|
+
# It is necessary to define the JSModuleLoaderDelegate protocol before we can use it as its not a public protocol, for original definition
|
|
501
|
+
# see: https://github.com/WebKit/WebKit/blob/3a620254c233064790f172eb54eee6db874be7b1/Source/JavaScriptCore/API/JSContextPrivate.h
|
|
502
|
+
JSModuleLoaderDelegate = objc.protocol("JSModuleLoaderDelegate", body=[
|
|
503
|
+
"@required",
|
|
504
|
+
"- (void)context:(JSContext *)context fetchModuleForIdentifier:(JSValue *)identifier withResolveHandler:(JSValue *)resolve andRejectHandler:(JSValue *)reject;",
|
|
505
|
+
"@optional",
|
|
506
|
+
"- (void)willEvaluateModule:(NSURL *)key;",
|
|
507
|
+
"- (void)didEvaluateModule:(NSURL *)key;"
|
|
508
|
+
], debug=False)
|
|
509
|
+
|
|
510
|
+
# forwards calls to loader
|
|
511
|
+
def JSCoreModuleLoaderDelegate_context_fetchModuleForIdentifier_withResolveHandler_andRejectHandler_(_self,_cmd, _ctx, _id, _resolve, _reject):
|
|
512
|
+
loader = ObjCInstance(_self)._pyinstance()
|
|
513
|
+
module = javascript_value(ObjCInstance(_id)).value
|
|
514
|
+
resolve = javascript_function(ObjCInstance(_resolve))
|
|
515
|
+
reject = javascript_function(ObjCInstance(_reject))
|
|
516
|
+
loader.fetch_module(module, resolve, reject)
|
|
517
|
+
|
|
518
|
+
f = JSCoreModuleLoaderDelegate_context_fetchModuleForIdentifier_withResolveHandler_andRejectHandler_
|
|
519
|
+
f.argtypes = [c_void_p,c_void_p,c_void_p,c_void_p]
|
|
520
|
+
f.encoding = "@:@@@@@"
|
|
521
|
+
|
|
522
|
+
# forward to loader when defined (though does not appear to be called)
|
|
523
|
+
def JSCoreModuleLoaderDelegate_willEvaluateModule_(_self,_cmd,_url):
|
|
524
|
+
loader = ObjCInstance(_self)._pyinstance()
|
|
525
|
+
handler = getattr(loader, 'will_eval_module')
|
|
526
|
+
if handler is not None:
|
|
527
|
+
handler(ObjCInstance(_url))
|
|
528
|
+
|
|
529
|
+
f = JSCoreModuleLoaderDelegate_willEvaluateModule_
|
|
530
|
+
f.argtypes = [c_void_p]
|
|
531
|
+
f.encoding = "@:@@"
|
|
532
|
+
|
|
533
|
+
# forward to loader when defined (though does not appear to be called)
|
|
534
|
+
def JSCoreModuleLoaderDelegate_didEvaluateModule_(_self,_cmd,_url):
|
|
535
|
+
loader = ObjCInstance(_self)._pyinstance()
|
|
536
|
+
handler = getattr(loader, 'did_eval_module')
|
|
537
|
+
if handler is not None:
|
|
538
|
+
handler(ObjCInstance(_url))
|
|
539
|
+
|
|
540
|
+
f = JSCoreModuleLoaderDelegate_didEvaluateModule_
|
|
541
|
+
f.argtypes = [c_void_p]
|
|
542
|
+
f.encoding = "@:@@"
|
|
543
|
+
|
|
544
|
+
# JSModuleLoaderDelegate protocol implementation
|
|
545
|
+
JSCoreModuleLoaderDelegate = objc.new_class("JSCoreModuleLoaderDelegate", protocols=[JSModuleLoaderDelegate], methods = [
|
|
546
|
+
JSCoreModuleLoaderDelegate_context_fetchModuleForIdentifier_withResolveHandler_andRejectHandler_,
|
|
547
|
+
JSCoreModuleLoaderDelegate_willEvaluateModule_,
|
|
548
|
+
JSCoreModuleLoaderDelegate_didEvaluateModule_
|
|
549
|
+
])
|
|
550
|
+
|
|
551
|
+
_runtime_vm = None
|
|
552
|
+
_runtimes = {}
|
|
553
|
+
_runtime_cleanups = []
|
|
554
|
+
@classmethod
|
|
555
|
+
def new_runtime(cls, runtime_class, *args, **kwargs):
|
|
556
|
+
if runtime_class is None:
|
|
557
|
+
raise ValueError("runtime_class must be specified")
|
|
558
|
+
return runtime_class(*args, **kwargs)
|
|
559
|
+
|
|
560
|
+
# runtime singleton access
|
|
561
|
+
@classmethod
|
|
562
|
+
def runtime(cls, runtime_class = None):
|
|
563
|
+
if runtime_class is None:
|
|
564
|
+
runtime_class = javascript_runtime
|
|
565
|
+
runtime = jscore._runtimes.get(runtime_class)
|
|
566
|
+
if runtime is None:
|
|
567
|
+
if cls._runtime_vm is None:
|
|
568
|
+
cls._runtime_vm = cls.vm_allocate()
|
|
569
|
+
runtime = cls.new_runtime(runtime_class, cls._runtime_vm)
|
|
570
|
+
jscore._runtimes[runtime_class] = runtime
|
|
571
|
+
return runtime
|
|
572
|
+
|
|
573
|
+
@classmethod
|
|
574
|
+
def vm_allocate(cls):
|
|
575
|
+
vm = jscore.JSVirtualMachine.alloc().init()
|
|
576
|
+
retain_global(vm)
|
|
577
|
+
return vm
|
|
578
|
+
|
|
579
|
+
@classmethod
|
|
580
|
+
def vm_deallocate(cls, vm):
|
|
581
|
+
release_global(vm)
|
|
582
|
+
|
|
583
|
+
@classmethod
|
|
584
|
+
def runtime_deallocate(cls, runtime, vm_owner):
|
|
585
|
+
vm = runtime.vm
|
|
586
|
+
if vm_owner:
|
|
587
|
+
cls.vm_deallocate(vm)
|
|
588
|
+
runtime.vm = None # always drop the vm reference as the runtime is done with it
|
|
589
|
+
runtime_scripts = list(runtime.scripts)
|
|
590
|
+
def cleanup():
|
|
591
|
+
released = [] # avoid releasing more than once
|
|
592
|
+
for script in runtime_scripts:
|
|
593
|
+
if not script in released:
|
|
594
|
+
if isinstance(script, jsscript_ref):
|
|
595
|
+
script.release()
|
|
596
|
+
else:
|
|
597
|
+
release_global(script)
|
|
598
|
+
released.append(script)
|
|
599
|
+
if vm_owner:
|
|
600
|
+
cleanup()
|
|
601
|
+
else:
|
|
602
|
+
cls._runtime_cleanups.append(cleanup)
|
|
603
|
+
key = runtime.__class__
|
|
604
|
+
rt = cls._runtimes.get(key)
|
|
605
|
+
if runtime is rt: # remove destroyed runtime if its a tracked singleton instance
|
|
606
|
+
del cls._runtimes[key]
|
|
607
|
+
if len(cls._runtimes) == 0:
|
|
608
|
+
cls.vm_deallocate(cls._runtime_vm) # if we destroyed the last singleton runtime reference cleanup vm
|
|
609
|
+
cls._runtime_vm = None
|
|
610
|
+
for cleanup in cls._runtime_cleanups:
|
|
611
|
+
cleanup()
|
|
612
|
+
cls._runtime_cleanups = []
|
|
613
|
+
|
|
614
|
+
_context_ref_context_lookup = {}
|
|
615
|
+
@classmethod
|
|
616
|
+
def context_allocate(cls, vm):
|
|
617
|
+
context = jscore.JSContext.alloc().initWithVirtualMachine_(vm)
|
|
618
|
+
retain_global(context)
|
|
619
|
+
context.setInspectable(True)
|
|
620
|
+
context_ref = context.JSGlobalContextRef()
|
|
621
|
+
cls._context_ref_context_lookup[context_ref.value] = context
|
|
622
|
+
return context
|
|
623
|
+
|
|
624
|
+
@classmethod
|
|
625
|
+
def context_deallocate(cls, context):
|
|
626
|
+
context_ref = context.JSGlobalContextRef()
|
|
627
|
+
del cls._context_ref_context_lookup[context_ref.value]
|
|
628
|
+
release_global(context)
|
|
629
|
+
|
|
630
|
+
@classmethod
|
|
631
|
+
def context_ref_to_context(cls, context_ref):
|
|
632
|
+
if context_ref is None:
|
|
633
|
+
return None
|
|
634
|
+
return cls._context_ref_context_lookup[context_ref.value]
|
|
635
|
+
|
|
636
|
+
@classmethod
|
|
637
|
+
def context_eval(cls, context, script, sourceUrl = None):
|
|
638
|
+
result = None
|
|
639
|
+
if sourceUrl is None or sourceUrl.strip() == '':
|
|
640
|
+
result = context.evaluateScript_(script)
|
|
641
|
+
else:
|
|
642
|
+
result = context.evaluateScript_withSourceUrl_(script, sourceUrl)
|
|
643
|
+
result = ObjCInstance(result)
|
|
644
|
+
ex = context.exception()
|
|
645
|
+
if ex is not None:
|
|
646
|
+
context.setException(None) # clear exception if set
|
|
647
|
+
return result, ex
|
|
648
|
+
|
|
649
|
+
# jscore values conversions
|
|
650
|
+
@classmethod
|
|
651
|
+
def jsstringref_to_py(cls, str_ref):
|
|
652
|
+
chars_len = jscore.JSStringGetLength(str_ref)
|
|
653
|
+
chars_ref = jscore.JSStringGetCharactersPtr(str_ref)
|
|
654
|
+
str_utf16 = string_at(chars_ref, chars_len*2)
|
|
655
|
+
str_decoded = str_utf16.decode('utf-16')
|
|
656
|
+
return str_decoded
|
|
657
|
+
|
|
658
|
+
@classmethod
|
|
659
|
+
def str_to_jsstringref(cls, str_py):
|
|
660
|
+
if str_py is None:
|
|
661
|
+
return None
|
|
662
|
+
str_py = str(str_py)
|
|
663
|
+
str_len = len(str_py)
|
|
664
|
+
str_utf16 = objc.c_array(str_py.encode("utf-16le"))
|
|
665
|
+
str_ref = jscore.JSStringCreateWithCharacters(str_utf16, str_len)
|
|
666
|
+
return cast(cls.JSStringRetain(str_ref), c_void_p)
|
|
667
|
+
|
|
668
|
+
@classmethod
|
|
669
|
+
def jsobjectref_keys(cls, context_ref, value_ref):
|
|
670
|
+
names = []
|
|
671
|
+
names_ref = jscore.JSObjectCopyPropertyNames(context_ref, value_ref)
|
|
672
|
+
count = jscore.JSPropertyNameArrayGetCount(names_ref)
|
|
673
|
+
for i in range(count):
|
|
674
|
+
str_ref = jscore.JSPropertyNameArrayGetNameAtIndex(names_ref, i)
|
|
675
|
+
name = cls.jsstringref_to_py(str_ref)
|
|
676
|
+
names.append(name)
|
|
677
|
+
jscore.JSPropertyNameArrayRelease(names_ref)
|
|
678
|
+
return names
|
|
679
|
+
|
|
680
|
+
@classmethod
|
|
681
|
+
def jsvalue_get_refs(cls, value):
|
|
682
|
+
context_ref = value.context().JSGlobalContextRef()
|
|
683
|
+
value_ref = value.JSValueRef()
|
|
684
|
+
return context_ref, value_ref
|
|
685
|
+
|
|
686
|
+
@classmethod
|
|
687
|
+
def jsvalue_jsobject_to_py(cls, value):
|
|
688
|
+
context_ref, value_ref = cls.jsvalue_get_refs(value)
|
|
689
|
+
if jscore.JSObjectIsFunction(context_ref, value_ref):
|
|
690
|
+
return javascript_function(value, context_ref, value_ref)
|
|
691
|
+
return cls.jsobjectref_to_py(context_ref, value_ref)
|
|
692
|
+
|
|
693
|
+
@classmethod
|
|
694
|
+
def jsvalue_to_py(cls, value):
|
|
695
|
+
if value is None or value.isNull():
|
|
696
|
+
return None
|
|
697
|
+
|
|
698
|
+
if javascript_value.is_undefined(value) or value.isUndefined():
|
|
699
|
+
return javascript_value.undefined
|
|
700
|
+
|
|
701
|
+
if value.isBoolean():
|
|
702
|
+
return value.toBool()
|
|
703
|
+
|
|
704
|
+
if value.isNumber() or value.isString() or value.isDate():
|
|
705
|
+
return objc.ns_to_py(value.toObject())
|
|
706
|
+
|
|
707
|
+
if value.isSymbol():
|
|
708
|
+
return javascript_symbol(value)
|
|
709
|
+
|
|
710
|
+
return cls.jsvalue_jsobject_to_py(value)
|
|
711
|
+
|
|
712
|
+
@classmethod
|
|
713
|
+
def jsvalue_is_object(cls, value):
|
|
714
|
+
if javascript_value.is_undefined(value) or not value.isObject():
|
|
715
|
+
return False
|
|
716
|
+
context_ref, value_ref = cls.jsvalue_get_refs(value)
|
|
717
|
+
if jscore.JSObjectIsFunction(context_ref, value_ref):
|
|
718
|
+
return False
|
|
719
|
+
return True
|
|
720
|
+
|
|
721
|
+
@classmethod
|
|
722
|
+
def jsvalue_is_array_type(cls, value, typedArrayType):
|
|
723
|
+
if not objc.ns_subclass_of(value, jscore.JSValue):
|
|
724
|
+
return False
|
|
725
|
+
context_ref, value_ref = cls.jsvalue_get_refs(value)
|
|
726
|
+
ex = c_void_p(None)
|
|
727
|
+
arrayType = cls.JSValueGetTypedArrayType(context_ref, value_ref, byref(ex))
|
|
728
|
+
return arrayType == typedArrayType
|
|
729
|
+
|
|
730
|
+
@classmethod
|
|
731
|
+
def jsobject_get_keys(cls, value):
|
|
732
|
+
if javascript_value.is_undefined(value) or not value.isObject():
|
|
733
|
+
return []
|
|
734
|
+
context_ref, value_ref = cls.jsvalue_get_refs(value)
|
|
735
|
+
if jscore.JSObjectIsFunction(context_ref, value_ref):
|
|
736
|
+
return []
|
|
737
|
+
return cls.jsobjectref_keys(context_ref, value_ref)
|
|
738
|
+
|
|
739
|
+
@classmethod
|
|
740
|
+
def jsobjectref_to_py(cls, context_ref, value_ref):
|
|
741
|
+
ex = c_void_p(None)
|
|
742
|
+
value_ref = cls.JSValueToObject(context_ref, value_ref, byref(ex))
|
|
743
|
+
if cls.JSObjectIsFunction(context_ref, value_ref):
|
|
744
|
+
str_ref = cls.JSValueToStringCopy(context_ref, value_ref, byref(ex))
|
|
745
|
+
source = None
|
|
746
|
+
if str_ref:
|
|
747
|
+
source = cls.jsstringref_to_py(str_ref)
|
|
748
|
+
return javascript_function(None, context_ref, value_ref, source)
|
|
749
|
+
names_ref = cls.JSObjectCopyPropertyNames(context_ref, value_ref)
|
|
750
|
+
count = cls.JSPropertyNameArrayGetCount(names_ref)
|
|
751
|
+
obj = None
|
|
752
|
+
if cls.JSValueIsArray(context_ref, value_ref):
|
|
753
|
+
obj = []
|
|
754
|
+
for i in range(count):
|
|
755
|
+
key_ref = cls.JSPropertyNameArrayGetNameAtIndex(names_ref, i)
|
|
756
|
+
jsvalue_ref = cls.JSObjectGetProperty(context_ref, value_ref, key_ref, byref(ex))
|
|
757
|
+
obj.append(cls.jsvalueref_to_py(context_ref, jsvalue_ref))
|
|
758
|
+
else:
|
|
759
|
+
prototype_ref = cls.JSObjectGetPrototype(context_ref, value_ref)
|
|
760
|
+
obj = cls.jsvalueref_to_py(context_ref, prototype_ref)
|
|
761
|
+
if javascript_value.is_null_or_undefined(obj):
|
|
762
|
+
obj = {}
|
|
763
|
+
for i in range(count):
|
|
764
|
+
key_ref = cls.JSPropertyNameArrayGetNameAtIndex(names_ref, i)
|
|
765
|
+
jsvalue_ref = cls.JSObjectGetProperty(context_ref, value_ref, key_ref, byref(ex))
|
|
766
|
+
key = cls.jsstringref_to_py(key_ref)
|
|
767
|
+
obj[key] = cls.jsvalueref_to_py(context_ref, jsvalue_ref)
|
|
768
|
+
cls.JSPropertyNameArrayRelease(names_ref)
|
|
769
|
+
return obj
|
|
770
|
+
|
|
771
|
+
@classmethod
|
|
772
|
+
def jsvalueref_to_py(cls, context_ref, value_ref):
|
|
773
|
+
if value_ref is None:
|
|
774
|
+
return None
|
|
775
|
+
if cls.JSValueIsUndefined(context_ref, value_ref):
|
|
776
|
+
return javascript_value.undefined
|
|
777
|
+
if cls.JSValueIsNull(context_ref, value_ref):
|
|
778
|
+
return None
|
|
779
|
+
if cls.JSValueIsBoolean(context_ref, value_ref):
|
|
780
|
+
return cls.JSValueToBoolean(context_ref, value_ref)
|
|
781
|
+
if cls.JSValueIsNumber(context_ref, value_ref):
|
|
782
|
+
ex = c_void_p(None)
|
|
783
|
+
return cls.JSValueToNumber(context_ref, value_ref, byref(ex))
|
|
784
|
+
if cls.JSValueIsString(context_ref, value_ref):
|
|
785
|
+
ex = c_void_p(None)
|
|
786
|
+
str_ref = cls.JSValueToStringCopy(context_ref, value_ref, byref(ex))
|
|
787
|
+
if str_ref:
|
|
788
|
+
return cls.jsstringref_to_py(str_ref)
|
|
789
|
+
return ""
|
|
790
|
+
if cls.JSValueIsDate(context_ref, value_ref):
|
|
791
|
+
ex = c_void_p(None)
|
|
792
|
+
str_ref = cls.JSValueCreateJSONString(context_ref, value_ref, 0, byref(ex))
|
|
793
|
+
json_date = cls.jsstringref_to_py(str_ref)
|
|
794
|
+
return datetime.strptime(json_date, '"%Y-%m-%dT%H:%M:%S.%fZ"').replace(tzinfo=timezone.utc)
|
|
795
|
+
if cls.JSValueIsSymbol(context_ref, value_ref):
|
|
796
|
+
ex = c_void_p(None)
|
|
797
|
+
str_ref = cls.JSValueToStringCopy(context_ref, value_ref, byref(ex))
|
|
798
|
+
symbol = cls.jsstringref_to_py(str_ref)
|
|
799
|
+
return javascript_symbol(symbol)
|
|
800
|
+
if cls.JSValueIsObject(context_ref, value_ref):
|
|
801
|
+
return cls.jsobjectref_to_py(context_ref, value_ref)
|
|
802
|
+
raise NotImplementedError("Unknown value_ref type")
|
|
803
|
+
|
|
804
|
+
@classmethod
|
|
805
|
+
def _py_to_jsvalueref(cls, context_ref, value, parent_ref = None):
|
|
806
|
+
if value is None:
|
|
807
|
+
return cls.JSValueMakeNull(context_ref)
|
|
808
|
+
if javascript_value.is_undefined(value):
|
|
809
|
+
return cls.JSValueMakeUndefined(context_ref)
|
|
810
|
+
if isinstance(value, c_void_p):
|
|
811
|
+
return value # assume a void pointer is a value ref
|
|
812
|
+
if objc.ns_subclass_of(value, cls.JSValue):
|
|
813
|
+
return value.JSValueRef() # return refs from existing JSValues
|
|
814
|
+
# convert
|
|
815
|
+
if isinstance(value, bool):
|
|
816
|
+
return cls.JSValueMakeBoolean(context_ref, value)
|
|
817
|
+
if isinstance(value, int) or isinstance(value, float):
|
|
818
|
+
return cls.JSValueMakeNumber(context_ref, value)
|
|
819
|
+
if isinstance(value, datetime):
|
|
820
|
+
value_utc = datetime.fromtimestamp(value.timestamp(), tz = timezone.utc)
|
|
821
|
+
value_str = value_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
822
|
+
str_ref = jscore.str_to_jsstringref(value_str)
|
|
823
|
+
return cls.JSValueMakeFromJSONString(context_ref, str_ref)
|
|
824
|
+
if isinstance(value, str):
|
|
825
|
+
str_ref = jscore.str_to_jsstringref(value)
|
|
826
|
+
return cls.JSValueMakeString(context_ref, str_ref)
|
|
827
|
+
if isinstance(value, javascript_function):
|
|
828
|
+
source = str(value)
|
|
829
|
+
value_ref = javascript_function.from_source(source, context_ref).compile()
|
|
830
|
+
return value_ref
|
|
831
|
+
if isinstance(value, javascript_callback):
|
|
832
|
+
value_ref = value.get_jsvalue_ref(context_ref, parent_ref)
|
|
833
|
+
return value_ref
|
|
834
|
+
if isinstance(value, bytes):
|
|
835
|
+
count = len(value)
|
|
836
|
+
ex_ref = c_void_p(None)
|
|
837
|
+
value_ref = cls.JSObjectMakeTypedArray(context_ref, cls.kJSTypedArrayTypeUint8Array, count, byref(ex_ref))
|
|
838
|
+
bytes_ptr = jscore.JSObjectGetTypedArrayBytesPtr(context_ref, value_ref, byref(ex_ref))
|
|
839
|
+
if bytes_ptr is not None:
|
|
840
|
+
memmove(bytes_ptr, value, count)
|
|
841
|
+
return value_ref
|
|
842
|
+
if objc.ns_subclass_of(value, NSData):
|
|
843
|
+
count = value.length()
|
|
844
|
+
ex_ref = c_void_p(None)
|
|
845
|
+
value_ref = cls.JSObjectMakeTypedArray(context_ref, cls.kJSTypedArrayTypeUint8Array, count, byref(ex_ref))
|
|
846
|
+
bytes_ptr = jscore.JSObjectGetTypedArrayBytesPtr(context_ref, value_ref, byref(ex_ref))
|
|
847
|
+
if bytes_ptr is not None:
|
|
848
|
+
value.getBytes_length_(bytes_ptr, count)
|
|
849
|
+
return value_ref
|
|
850
|
+
if isinstance(value, list):
|
|
851
|
+
ex_ref = c_void_p(None)
|
|
852
|
+
value_ref = cls.JSObjectMakeArray(context_ref, 0, None, byref(ex_ref))
|
|
853
|
+
count = len(value)
|
|
854
|
+
for i in range(count):
|
|
855
|
+
val_ref = cls.py_to_jsvalueref(context_ref, value[i], value_ref)
|
|
856
|
+
cls.JSObjectSetPropertyAtIndex(context_ref, value_ref, i, val_ref, byref(ex_ref))
|
|
857
|
+
return value_ref
|
|
858
|
+
if isinstance(value, dict):
|
|
859
|
+
value_ref = cls.JSObjectMake(context_ref, None, None)
|
|
860
|
+
ex_ref = c_void_p(None)
|
|
861
|
+
for k,v in value.items():
|
|
862
|
+
key_ref = cls.str_to_jsstringref(k)
|
|
863
|
+
val_ref = cls.py_to_jsvalueref(context_ref, v, value_ref)
|
|
864
|
+
cls.JSObjectSetProperty(context_ref, value_ref, key_ref, val_ref, 0, byref(ex_ref))
|
|
865
|
+
return value_ref
|
|
866
|
+
typ = type(value)
|
|
867
|
+
raise NotImplementedError(f"Type '{typ}' for value '{value}' not supported.")
|
|
868
|
+
|
|
869
|
+
@classmethod
|
|
870
|
+
def py_to_jsvalueref(cls, context_ref, value, parent_ref = None):
|
|
871
|
+
value_ref = cls._py_to_jsvalueref(context_ref, value, parent_ref)
|
|
872
|
+
#cls.JSValueProtect(context_ref, value_ref)
|
|
873
|
+
return cast(value_ref, c_void_p) # ensure a c_void_p
|
|
874
|
+
|
|
875
|
+
@classmethod
|
|
876
|
+
def py_to_jsvalue(cls, context, value, parent = None):
|
|
877
|
+
if value is None:
|
|
878
|
+
return cls.JSValue.valueWithNullInContext_(context)
|
|
879
|
+
if javascript_value.is_undefined(value):
|
|
880
|
+
return cls.JSValue.valueWithUndefinedInContext_(context)
|
|
881
|
+
if objc.ns_subclass_of(value, jscore.JSValue):
|
|
882
|
+
return value # pass back jsvalue instances as-is
|
|
883
|
+
if isinstance(value, bool):
|
|
884
|
+
return cls.JSValue.valueWithBool_inContext_(value, context)
|
|
885
|
+
if isinstance(value, int) or isinstance(value, float) or isinstance(value, str):
|
|
886
|
+
return cls.JSValue.valueWithObject_inContext_(ns(value), context)
|
|
887
|
+
if isinstance(value, datetime):
|
|
888
|
+
timestamp = value.timestamp()
|
|
889
|
+
return cls.JSValue.valueWithObject_inContext_(cls.initWithTimeIntervalSince1970_(timestamp), context)
|
|
890
|
+
if isinstance(value, javascript_function):
|
|
891
|
+
jsvalue = value.jsvalue
|
|
892
|
+
if jsvalue is not None:
|
|
893
|
+
return jsvalue
|
|
894
|
+
if value.is_native:
|
|
895
|
+
raise ValueError("Cannot evaluate native functions (this shouldn't be reachable!')")
|
|
896
|
+
source = str(value)
|
|
897
|
+
# this obtains a new jsvalue for a javascript function by evaluating it in the context,
|
|
898
|
+
# further escaping and resolving may be required here...
|
|
899
|
+
jsvalue, ex = cls.context_eval(context, f'(function() {{ return ({source}); }})()')
|
|
900
|
+
return jsvalue
|
|
901
|
+
if isinstance(value, javascript_callback):
|
|
902
|
+
jsvalue = value.get_jsvalue(context, parent)
|
|
903
|
+
return jsvalue
|
|
904
|
+
if isinstance(value, bytes):
|
|
905
|
+
count = len(value)
|
|
906
|
+
jsvalue = cls.context_eval(context, f"new Uint8Array({count});")
|
|
907
|
+
context_ref, value_ref = cls.jsvalue_get_refs(jsvalue)
|
|
908
|
+
ex_ref = c_void_p(None)
|
|
909
|
+
bytes_ptr = jscore.JSObjectGetTypedArrayBytesPtr(context_ref, value_ref, byref(ex_ref))
|
|
910
|
+
if bytes_ptr is not None:
|
|
911
|
+
memmove(bytes_ptr, value, count)
|
|
912
|
+
return jsvalue
|
|
913
|
+
if objc.ns_subclass_of(value, NSData):
|
|
914
|
+
count = value.length()
|
|
915
|
+
jsvalue = cls.context_eval(context, f"new Uint8Array({count});")
|
|
916
|
+
context_ref, value_ref = cls.jsvalue_get_refs(jsvalue)
|
|
917
|
+
ex_ref = c_void_p(None)
|
|
918
|
+
bytes_ptr = jscore.JSObjectGetTypedArrayBytesPtr(context_ref, value_ref, byref(ex_ref))
|
|
919
|
+
if bytes_ptr is not None:
|
|
920
|
+
value.getBytes_length_(bytes_ptr, count)
|
|
921
|
+
return jsvalue
|
|
922
|
+
if isinstance(value, dict):
|
|
923
|
+
jsvalue = cls.JSValue.valueWithNewObjectInContext_(context)
|
|
924
|
+
for k,v in value.items():
|
|
925
|
+
val = cls.py_to_jsvalue(context, v, jsvalue)
|
|
926
|
+
jsvalue.setValue_forProperty_(val, k)
|
|
927
|
+
return jsvalue
|
|
928
|
+
if isinstance(value, list):
|
|
929
|
+
jsvalue = cls.JSValue.valueWithNewArrayInContext_(context)
|
|
930
|
+
for i in range(len(value)):
|
|
931
|
+
val = cls.py_to_jsvalue(context, value[i], jsvalue)
|
|
932
|
+
jsvalue.setValue_atIndex_(i, val)
|
|
933
|
+
return jsvalue
|
|
934
|
+
typ = type(value)
|
|
935
|
+
raise NotImplementedError(f"Type '{typ}' for value '{value}' not supported.")
|
|
936
|
+
|
|
937
|
+
@classmethod
|
|
938
|
+
def py_to_js(cls, value):
|
|
939
|
+
value = jsobject_accessor.unwrap(value)
|
|
940
|
+
return json.dumps(value, cls=javascript_encoder)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
class javascript_encoder(json.JSONEncoder):
|
|
944
|
+
# An overriden json encoder to write javascript objects encoding functions / raw literal js
|
|
945
|
+
def __init__(self, *args, **kwargs):
|
|
946
|
+
super().__init__(*args, **kwargs)
|
|
947
|
+
self.raw = False
|
|
948
|
+
|
|
949
|
+
def default(self, obj):
|
|
950
|
+
if javascript_value.is_undefined(obj):
|
|
951
|
+
return None
|
|
952
|
+
elif isinstance(obj, datetime):
|
|
953
|
+
self.raw = True
|
|
954
|
+
return obj.replace(tzinfo=timezone.utc).strftime('new Date("%Y-%m-%dT%H:%M:%S.%fZ")')
|
|
955
|
+
elif isinstance(obj, bytes):
|
|
956
|
+
self.raw = True
|
|
957
|
+
return "".join(["new Uint8Array(",str(list(obj)),")"])
|
|
958
|
+
elif isinstance(obj, javascript_function):
|
|
959
|
+
self.raw = True
|
|
960
|
+
return str(obj)
|
|
961
|
+
return json.JSONEncoder.default(self, obj)
|
|
962
|
+
|
|
963
|
+
def raw_unescape(self, chunk):
|
|
964
|
+
raw = json.loads(chunk) #unescape
|
|
965
|
+
return str(raw) # ensure string result
|
|
966
|
+
|
|
967
|
+
def encode(self, o):
|
|
968
|
+
chunks = []
|
|
969
|
+
for chunk in super().iterencode(o):
|
|
970
|
+
if self.raw: # handle chunk as a literal raw string
|
|
971
|
+
chunk = self.raw_unescape(chunk) #unescape the string
|
|
972
|
+
self.raw = False
|
|
973
|
+
chunks.append(chunk)
|
|
974
|
+
return ''.join(chunks)
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
# types for js values
|
|
978
|
+
class javascript_undefined_value:
|
|
979
|
+
def __repr__(self):
|
|
980
|
+
return "undefined"
|
|
981
|
+
|
|
982
|
+
# not sure what this is...
|
|
983
|
+
class javascript_symbol:
|
|
984
|
+
def __init__(self, symbol):
|
|
985
|
+
self.symbol = symbol
|
|
986
|
+
print(f"javascript_symbol {symbol}")
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
class javascript_function:
|
|
990
|
+
def __init__(self, jsvalue = None, context_ref = None, value_ref = None, source = None):
|
|
991
|
+
self.jsvalue = jsvalue
|
|
992
|
+
self.context_ref = context_ref
|
|
993
|
+
self.value_ref = value_ref
|
|
994
|
+
self.source = source
|
|
995
|
+
|
|
996
|
+
def compile(self, context_ref = None):
|
|
997
|
+
if context_ref is None:
|
|
998
|
+
context_ref = self.context_ref
|
|
999
|
+
if context_ref is None:
|
|
1000
|
+
raise ImportError("Cannot compile function from source without context_ref")
|
|
1001
|
+
if self.source is None:
|
|
1002
|
+
raise ImportError("Cannot compile function with no source")
|
|
1003
|
+
self.source = self.source.strip()
|
|
1004
|
+
name_match = re.match("function([^\(]*)\(", self.source)
|
|
1005
|
+
fn_name = name_match.group(0).strip()
|
|
1006
|
+
params_match = re.match("function[^\(]*\(([^\)]*)\)", self.source)
|
|
1007
|
+
params = params_match.group(0).split(",")
|
|
1008
|
+
params_refs = []
|
|
1009
|
+
for p in params:
|
|
1010
|
+
param = p.strip()
|
|
1011
|
+
if param != "":
|
|
1012
|
+
param_ref = jscore.str_to_jsstringref(param)
|
|
1013
|
+
params_refs.append(param_ref)
|
|
1014
|
+
params_refs = objc.c_array(params_refs)
|
|
1015
|
+
name_ref = jscore.str_to_jsstringref(fn_name)
|
|
1016
|
+
body = body[body.index('{'):body.rindex('}')]
|
|
1017
|
+
body_ref = jscore.str_to_jsstringref(body)
|
|
1018
|
+
ex_ref = c_void_p(None)
|
|
1019
|
+
self.value_ref = jscore.JSObjectMakeFunction(context_ref, name_ref, params_count, params_refs, body_ref, None, 0, by_ref(ex_ref))
|
|
1020
|
+
self.context_ref = context_ref
|
|
1021
|
+
if ex_ref.value is not None:
|
|
1022
|
+
exception = jscore.jsvalueref_to_py(context_ref, ex_ref)
|
|
1023
|
+
raise ImportError("Exception compiling function: {exception}")
|
|
1024
|
+
return self.value_ref
|
|
1025
|
+
|
|
1026
|
+
def js_arg(self, context, arg):
|
|
1027
|
+
if javascript_value.is_null_or_undefined(arg):
|
|
1028
|
+
return jscore.py_to_jsvalue(context, arg)
|
|
1029
|
+
if isinstance(arg, dict):
|
|
1030
|
+
copy = {}
|
|
1031
|
+
for k,v in arg.items():
|
|
1032
|
+
copy[k]=self.js_arg(context, v)
|
|
1033
|
+
return copy
|
|
1034
|
+
if isinstance(arg, list):
|
|
1035
|
+
copy = []
|
|
1036
|
+
for i in range(len(arg)):
|
|
1037
|
+
copy.append(self.js_arg(context, arg[i]))
|
|
1038
|
+
return copy
|
|
1039
|
+
if isinstance(arg, javascript_function):
|
|
1040
|
+
return jscore.py_to_jsvalue(context, arg)
|
|
1041
|
+
return arg
|
|
1042
|
+
|
|
1043
|
+
def js_args(self, context, args):
|
|
1044
|
+
args = list(args)
|
|
1045
|
+
args = self.js_arg(context, args)
|
|
1046
|
+
nsargs = ns(args)
|
|
1047
|
+
return nsargs
|
|
1048
|
+
|
|
1049
|
+
def call(self, *args):
|
|
1050
|
+
if self.jsvalue is not None:
|
|
1051
|
+
context = self.jsvalue.context()
|
|
1052
|
+
nsargs = self.js_args(context, args)
|
|
1053
|
+
self.jsvalue.context().setException_(None)
|
|
1054
|
+
value = self.jsvalue.callWithArguments_(nsargs)
|
|
1055
|
+
exception = self.jsvalue.context().exception()
|
|
1056
|
+
if exception is not None:
|
|
1057
|
+
self.jsvalue.context().setException_(None)
|
|
1058
|
+
raise Exception(str(exception))
|
|
1059
|
+
return javascript_value(value)
|
|
1060
|
+
|
|
1061
|
+
if self.source is not None and self.value_ref is None:
|
|
1062
|
+
if self.context_ref is not None:
|
|
1063
|
+
self.compile()
|
|
1064
|
+
else:
|
|
1065
|
+
raise NotImplementedError("Cannot call source only functions without a context_ref")
|
|
1066
|
+
|
|
1067
|
+
if self.context_ref is not None and self.value_ref is not None:
|
|
1068
|
+
count = len(args)
|
|
1069
|
+
args_ref = None
|
|
1070
|
+
if count > 0:
|
|
1071
|
+
args_ref = objc.c_array(count, lambda i: jscore.py_to_jsvalueref(self.context_ref, args[i]))
|
|
1072
|
+
this_ref = None
|
|
1073
|
+
exception_ref = c_void_p(None)
|
|
1074
|
+
value_ref = jscore.JSObjectCallAsFunction(self.context_ref, self.value_ref, this_ref, count, args_ref, byref(exception_ref))
|
|
1075
|
+
if exception_ref.value is not None:
|
|
1076
|
+
raise Exception(jscore.jsvalueref_to_py(exception_ref))
|
|
1077
|
+
return javascript_value(None, self.context_ref, value_ref)
|
|
1078
|
+
|
|
1079
|
+
raise NotImplementedError("Cannot call this type of javascript_function")
|
|
1080
|
+
|
|
1081
|
+
@property
|
|
1082
|
+
def is_native(self):
|
|
1083
|
+
repr = str(self).strip()
|
|
1084
|
+
return re.fullmatch("function[^\{]+\{[^\[]+\[native code\][^\}]+}", repr) is not None
|
|
1085
|
+
|
|
1086
|
+
def __call__(self, *args):
|
|
1087
|
+
return self.call(*args).value
|
|
1088
|
+
|
|
1089
|
+
def __repr__(self):
|
|
1090
|
+
if self.source is not None:
|
|
1091
|
+
return self.source
|
|
1092
|
+
if self.jsvalue is not None:
|
|
1093
|
+
return str(self.jsvalue)
|
|
1094
|
+
|
|
1095
|
+
@classmethod
|
|
1096
|
+
def from_source(cls, source, context = None):
|
|
1097
|
+
context_ref = None
|
|
1098
|
+
if isinstance(context, c_void_p):
|
|
1099
|
+
context_ref = context
|
|
1100
|
+
elif context is not None:
|
|
1101
|
+
if isinstance(context, jscore_context):
|
|
1102
|
+
context = context.context
|
|
1103
|
+
if objc.ns_subclass_of(context, jscore.JSContext):
|
|
1104
|
+
context_ref = context.JSGlobalContextRef()
|
|
1105
|
+
return cls(source=source, context_ref=context_ref)
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
class javascript_callback:
|
|
1109
|
+
def __init__(self, callback, name = None):
|
|
1110
|
+
self.callback = callback
|
|
1111
|
+
self.name = name
|
|
1112
|
+
self.callback_ref = None
|
|
1113
|
+
self.context_ref = None
|
|
1114
|
+
self.value_ref = None
|
|
1115
|
+
self._jsvalue = None
|
|
1116
|
+
|
|
1117
|
+
def compile(self, context_ref = None, parent_ref = None):
|
|
1118
|
+
if context_ref is None:
|
|
1119
|
+
context_ref = self.context_ref
|
|
1120
|
+
if context_ref is None:
|
|
1121
|
+
raise Exception("Context is required to compile callbacks")
|
|
1122
|
+
if self.value_ref is not None:
|
|
1123
|
+
raise Exception("Cannot recompile callbacks")
|
|
1124
|
+
if self.name is None:
|
|
1125
|
+
self.name = javascript_callback.unique_name()
|
|
1126
|
+
name_ref = jscore.str_to_jsstringref(self.name)
|
|
1127
|
+
self.callback_ref = jscore.JSObjectCallAsFunctionCallback(self._invoke_callback)
|
|
1128
|
+
value_ref = jscore.JSObjectMakeFunctionWithCallback(context_ref, name_ref, self.callback_ref)
|
|
1129
|
+
jscore.JSValueProtect(context_ref, value_ref)
|
|
1130
|
+
self.context_ref = context_ref
|
|
1131
|
+
self.value_ref = value_ref
|
|
1132
|
+
if parent_ref is None:
|
|
1133
|
+
parent_ref = jscore.JSContextGetGlobalObject(self.context_ref)
|
|
1134
|
+
ex = c_void_p(None)
|
|
1135
|
+
jscore.JSObjectSetProperty(self.context_ref, parent_ref, name_ref, value_ref, 0, byref(ex))
|
|
1136
|
+
|
|
1137
|
+
def get_jsvalue_ref(self, context_ref, parent_ref = None):
|
|
1138
|
+
if self.context_ref is not None and self.context_ref != context_ref:
|
|
1139
|
+
raise Exception("Cannot change context")
|
|
1140
|
+
if self.value_ref is None:
|
|
1141
|
+
self.compile(context_ref, parent_ref)
|
|
1142
|
+
return self.value_ref
|
|
1143
|
+
|
|
1144
|
+
def get_jsvalue(self, context, parent = None):
|
|
1145
|
+
context_ref = context.JSGlobalContextRef()
|
|
1146
|
+
parent_ref = None
|
|
1147
|
+
if parent is not None:
|
|
1148
|
+
parent_ref = parent.JSValueRef()
|
|
1149
|
+
if self._jsvalue is None:
|
|
1150
|
+
value_ref = self.get_jsvalue_ref(context_ref, parent_ref)
|
|
1151
|
+
if parent is None:
|
|
1152
|
+
parent = context.globalObject()
|
|
1153
|
+
self._jsvalue = parent.valueForProperty(self.name) # work around to obtain a jsvalue from jsvalue_ref
|
|
1154
|
+
return self._jsvalue
|
|
1155
|
+
|
|
1156
|
+
def _invoke_callback(self, ctx, funcObj, thisObj, args_count, args, ex):
|
|
1157
|
+
ctx = c_void_p(ctx)
|
|
1158
|
+
funcObj = c_void_p(funcObj)
|
|
1159
|
+
thisObj = c_void_p(thisObj)
|
|
1160
|
+
callback_args = []
|
|
1161
|
+
for i in range(args_count):
|
|
1162
|
+
arg = c_void_p.from_address(args + (i * sizeof(c_void_p)))
|
|
1163
|
+
arg_value = jscore.jsvalueref_to_py(ctx, arg)
|
|
1164
|
+
callback_args.append(arg_value)
|
|
1165
|
+
returnValue = self.callback(*callback_args)
|
|
1166
|
+
returnJSValue_ref = jscore.py_to_jsvalueref(ctx, returnValue)
|
|
1167
|
+
return returnJSValue_ref.value
|
|
1168
|
+
|
|
1169
|
+
_name_count = 0
|
|
1170
|
+
@classmethod
|
|
1171
|
+
def unique_name(cls):
|
|
1172
|
+
name = f"python_callback_{cls._name_count}"
|
|
1173
|
+
cls._name_count = cls._name_count + 1
|
|
1174
|
+
return name
|
|
1175
|
+
|
|
1176
|
+
@classmethod
|
|
1177
|
+
def is_callable(cls, func):
|
|
1178
|
+
return not isinstance(func, javascript_function) and callable(func)
|
|
1179
|
+
|
|
1180
|
+
@classmethod
|
|
1181
|
+
def wrap(cls, context, value, name = None):
|
|
1182
|
+
if javascript_callback.is_callable(value):
|
|
1183
|
+
return context.callback(value, name)
|
|
1184
|
+
if isinstance(value, dict):
|
|
1185
|
+
for k, v in value.items():
|
|
1186
|
+
value[k] = cls.wrap(context, v, k)
|
|
1187
|
+
elif isinstance(value, list):
|
|
1188
|
+
for i in range(len(value)):
|
|
1189
|
+
k = str(i)
|
|
1190
|
+
v = value[i]
|
|
1191
|
+
value[i] = cls.wrap(context, v, k)
|
|
1192
|
+
return value
|
|
1193
|
+
|
|
1194
|
+
class javascript_object(dict):
|
|
1195
|
+
def __init__(self, *args, **kwargs):
|
|
1196
|
+
super().__init__(*args, **kwargs)
|
|
1197
|
+
self.___init___ = True
|
|
1198
|
+
|
|
1199
|
+
def __getattr__(self, key):
|
|
1200
|
+
value = self.get(key, javascript_value.undefined)
|
|
1201
|
+
if isinstance(value, dict):
|
|
1202
|
+
value = javascript_object(value)
|
|
1203
|
+
return value
|
|
1204
|
+
|
|
1205
|
+
def __setattr__(self, key, value):
|
|
1206
|
+
if not self.__dict__.get("___init___", False):
|
|
1207
|
+
super().__setattr__(key, value)
|
|
1208
|
+
else:
|
|
1209
|
+
self[key] = value
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
class javascript_value:
|
|
1213
|
+
undefined = javascript_undefined_value()
|
|
1214
|
+
@classmethod
|
|
1215
|
+
def is_undefined(cls, value):
|
|
1216
|
+
return value is cls.undefined
|
|
1217
|
+
|
|
1218
|
+
@classmethod
|
|
1219
|
+
def is_null(cls, value):
|
|
1220
|
+
return value is None
|
|
1221
|
+
|
|
1222
|
+
@classmethod
|
|
1223
|
+
def is_null_or_undefined(cls,value):
|
|
1224
|
+
return cls.is_null(value) or cls.is_undefined(value)
|
|
1225
|
+
|
|
1226
|
+
def __init__(self, jsvalue = None, context_ref = None, value_ref = None):
|
|
1227
|
+
if jsvalue is None and context_ref is None and value_ref is None:
|
|
1228
|
+
raise ArgumentError("Either jsvalue or context_ref and value_ref must be specified")
|
|
1229
|
+
self.jsvalue = jsvalue
|
|
1230
|
+
self.context_ref = context_ref
|
|
1231
|
+
self.value_ref = value_ref
|
|
1232
|
+
self._val = None
|
|
1233
|
+
self._cached = False
|
|
1234
|
+
|
|
1235
|
+
@property
|
|
1236
|
+
def value(self):
|
|
1237
|
+
if not self._cached:
|
|
1238
|
+
if self.jsvalue is not None:
|
|
1239
|
+
self._val = jscore.jsvalue_to_py(self.jsvalue)
|
|
1240
|
+
elif self.context_ref is not None and self.value_ref is not None:
|
|
1241
|
+
self._val = jscore.jsvalueref_to_py(self.context_ref, self.value_ref)
|
|
1242
|
+
else:
|
|
1243
|
+
self._val = javascript_value.undefined
|
|
1244
|
+
if isinstance(self._val, dict):
|
|
1245
|
+
self._val = javascript_object(self._val)
|
|
1246
|
+
self._cached = True
|
|
1247
|
+
return self._val
|
|
1248
|
+
|
|
1249
|
+
def __repr__(self):
|
|
1250
|
+
return str(self.value)
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
class jsscript_ref:
|
|
1254
|
+
def __init__(self, runtime, url, source):
|
|
1255
|
+
self.runtime = runtime
|
|
1256
|
+
self.vm = runtime.vm
|
|
1257
|
+
self.context_group_ref = self.vm.JSContextGroupRef()
|
|
1258
|
+
self.url = url
|
|
1259
|
+
self.source = source
|
|
1260
|
+
self.url_ref = jscore.str_to_jsstringref(url)
|
|
1261
|
+
self.source_ref = jscore.str_to_jsstringref(source)
|
|
1262
|
+
error_ref = c_void_p(None)
|
|
1263
|
+
error_line = c_void_p(0)
|
|
1264
|
+
script_ref = jscore.JSScriptCreateFromString(self.context_group_ref, self.url_ref, 0, self.source_ref, byref(error_ref), byref(error_line))
|
|
1265
|
+
self.script_ref = script_ref
|
|
1266
|
+
self.error_ref = error_ref
|
|
1267
|
+
self.error_line = error_line
|
|
1268
|
+
if self.script_ref:
|
|
1269
|
+
jscore.JSScriptRetain(self.script_ref)
|
|
1270
|
+
|
|
1271
|
+
def release(self):
|
|
1272
|
+
if self.runtime.vm is not None:
|
|
1273
|
+
raise Exception("VM must be released before releasing scripts")
|
|
1274
|
+
jscore.JSScriptRelease(self.script_ref) # must outlive VM
|
|
1275
|
+
jscore.JSStringRelease(self.source_ref)
|
|
1276
|
+
jscore.JSStringRelease(self.url_ref)
|
|
1277
|
+
|
|
1278
|
+
def eval(self, context):
|
|
1279
|
+
context = context.context
|
|
1280
|
+
context_ref = context.JSGlobalContextRef()
|
|
1281
|
+
if not self.script_ref:
|
|
1282
|
+
line = self.error_line
|
|
1283
|
+
exception = jscore.jsvalueref_to_py(context_ref, self.error_ref)
|
|
1284
|
+
raise ImportError(f"Error importing script at {line}, {exception}")
|
|
1285
|
+
this_ref = context.globalObject()
|
|
1286
|
+
this_ref = this_ref.JSValueRef()
|
|
1287
|
+
exception_ref = c_void_p(None)
|
|
1288
|
+
value_ref = jscore.JSScriptEvaluate(context_ref, self.script_ref, this_ref, byref(exception_ref))
|
|
1289
|
+
value = jscore.jsvalueref_to_py(context_ref, value_ref)
|
|
1290
|
+
exception = jscore.jsvalueref_to_py(context_ref, exception_ref)
|
|
1291
|
+
return value, exception
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
class jscore_module_loader:
|
|
1295
|
+
def __init__(self, context):
|
|
1296
|
+
self.runtime = context.runtime
|
|
1297
|
+
self.pycontext = context
|
|
1298
|
+
self.context = context.context
|
|
1299
|
+
self.scripts = {}
|
|
1300
|
+
self.modules = {}
|
|
1301
|
+
self.sources = {}
|
|
1302
|
+
self.delegate = jscore.JSCoreModuleLoaderDelegate.alloc().init().autorelease()
|
|
1303
|
+
retain_global(self.delegate)
|
|
1304
|
+
self.delegate._pyinstance = weakref.ref(self)
|
|
1305
|
+
self.context.setModuleLoaderDelegate_(self.delegate)
|
|
1306
|
+
self.evaluated = {}
|
|
1307
|
+
|
|
1308
|
+
def release(self):
|
|
1309
|
+
self.context.setModuleLoaderDelegate_(None)
|
|
1310
|
+
release_global(self.delegate)
|
|
1311
|
+
self.delegate = None
|
|
1312
|
+
|
|
1313
|
+
def fetch_module(self, module, resolve, reject):
|
|
1314
|
+
evaluated = self.evaluated.get(module)
|
|
1315
|
+
if evaluated is not None:
|
|
1316
|
+
if evaluated.exception is None:
|
|
1317
|
+
resolve(evaluated.jsvalue)
|
|
1318
|
+
else:
|
|
1319
|
+
reject(evaluated.exception)
|
|
1320
|
+
return
|
|
1321
|
+
script = self.modules.get(module)
|
|
1322
|
+
if script is None:
|
|
1323
|
+
script = self.load_file(module, jscore.kJSScriptTypeModule)
|
|
1324
|
+
source = self.sources.get(module)
|
|
1325
|
+
if script is not None and source is not None:
|
|
1326
|
+
result = self.pycontext.eval(source)
|
|
1327
|
+
exception = result.exception
|
|
1328
|
+
if exception is not None:
|
|
1329
|
+
reject(exception)
|
|
1330
|
+
print(f"Module load failed {module} {exception}")
|
|
1331
|
+
else:
|
|
1332
|
+
resolve(result.jsvalue)
|
|
1333
|
+
print(f"Module load success {module}")
|
|
1334
|
+
self.evaluated[module] = result
|
|
1335
|
+
else:
|
|
1336
|
+
reject(f"Module load failed {module}")
|
|
1337
|
+
print(f"Module load failed {module}")
|
|
1338
|
+
|
|
1339
|
+
def will_eval_module(self, url):
|
|
1340
|
+
print(f"will eval {url}")
|
|
1341
|
+
|
|
1342
|
+
def did_eval_module(self, url):
|
|
1343
|
+
print(f"did eval {url}")
|
|
1344
|
+
|
|
1345
|
+
def preprocess_source(self, scriptType, path, source):
|
|
1346
|
+
return source
|
|
1347
|
+
|
|
1348
|
+
def load_script_source(self, script, scriptType, path, sourceUrl, source = None):
|
|
1349
|
+
# we have to use lookups until we can decode strings directly from either:
|
|
1350
|
+
# WTF::String on script.source()
|
|
1351
|
+
# JSC:JSSourceCode* on script.jsSourceCode()
|
|
1352
|
+
# JSC::SourceCode on script.sourceCode()
|
|
1353
|
+
file_path = self.runtime.get_file_path(path)
|
|
1354
|
+
path_no_ext = str(file_path.with_suffix(''))
|
|
1355
|
+
if source is None:
|
|
1356
|
+
with open(file_path) as script_file:
|
|
1357
|
+
source = script_file.read()
|
|
1358
|
+
source = self.preprocess_source(scriptType, file_path, source)
|
|
1359
|
+
sources = self.sources
|
|
1360
|
+
sources[path] = source
|
|
1361
|
+
sources[path_no_ext] = source
|
|
1362
|
+
sources[f"file://{path_no_ext}"] = source
|
|
1363
|
+
sources[sourceUrl] = source
|
|
1364
|
+
|
|
1365
|
+
lookup = self.modules if scriptType == jscore.kJSScriptTypeModule else self.scripts
|
|
1366
|
+
lookup[source] = script
|
|
1367
|
+
lookup[path] = script
|
|
1368
|
+
lookup[path_no_ext] = script
|
|
1369
|
+
lookup[f"file://{path_no_ext}"] = script
|
|
1370
|
+
lookup[sourceUrl] = script
|
|
1371
|
+
|
|
1372
|
+
def load_source(self, source, scriptType, modulePath = None):
|
|
1373
|
+
lookup = self.modules if scriptType == jscore.kJSScriptTypeModule else self.scripts
|
|
1374
|
+
script = lookup.get(source)
|
|
1375
|
+
path = None
|
|
1376
|
+
if script is None:
|
|
1377
|
+
path = self.runtime.get_module_path(source, modulePath)
|
|
1378
|
+
script = lookup.get(f"file://{path}")
|
|
1379
|
+
if script is not None:
|
|
1380
|
+
return script
|
|
1381
|
+
script, sourceUrl, exception = self.runtime.load_source(source, scriptType, path)
|
|
1382
|
+
if exception is not None:
|
|
1383
|
+
raise ImportError(exception)
|
|
1384
|
+
self.load_script_source(script, scriptType, path, sourceUrl, source)
|
|
1385
|
+
return script
|
|
1386
|
+
|
|
1387
|
+
def load_file(self, path, scriptType):
|
|
1388
|
+
lookup = self.modules if scriptType == jscore.kJSScriptTypeModule else self.scripts
|
|
1389
|
+
script = lookup.get(path)
|
|
1390
|
+
if script is None:
|
|
1391
|
+
path = self.runtime.get_file_path(path)
|
|
1392
|
+
path = str(path)
|
|
1393
|
+
script = lookup.get(path)
|
|
1394
|
+
if script is not None:
|
|
1395
|
+
return script
|
|
1396
|
+
script, sourceUrl, exception = self.runtime.load_file(path, scriptType)
|
|
1397
|
+
if exception is not None:
|
|
1398
|
+
raise ImportError(exception)
|
|
1399
|
+
self.load_script_source(script, scriptType, path, sourceUrl)
|
|
1400
|
+
return script
|
|
1401
|
+
|
|
1402
|
+
|
|
1403
|
+
# base runtime framework
|
|
1404
|
+
class jscore_context:
|
|
1405
|
+
def __init__(self, runtime):
|
|
1406
|
+
self.runtime = runtime
|
|
1407
|
+
self.context = None
|
|
1408
|
+
self.depth = 0
|
|
1409
|
+
self.callbacks = None
|
|
1410
|
+
|
|
1411
|
+
def allocate(self):
|
|
1412
|
+
if self.context is not None:
|
|
1413
|
+
raise Exception("Context already allocated. Do not call allocate/deallocate manually.")
|
|
1414
|
+
self.context = jscore.context_allocate(self.runtime.vm)
|
|
1415
|
+
self.loader = jscore_module_loader(self)
|
|
1416
|
+
self.callbacks = {}
|
|
1417
|
+
|
|
1418
|
+
def deallocate(self):
|
|
1419
|
+
if self.context is None:
|
|
1420
|
+
raise Exception("Context already deallocated. Do not call allocate/deallocate manually.")
|
|
1421
|
+
self.loader.release()
|
|
1422
|
+
self.loader = None
|
|
1423
|
+
jscore.context_deallocate(self.context)
|
|
1424
|
+
self.context = None
|
|
1425
|
+
self.callbacks = None
|
|
1426
|
+
|
|
1427
|
+
def alloc(self):
|
|
1428
|
+
if self.context is not None:
|
|
1429
|
+
return
|
|
1430
|
+
self.allocate()
|
|
1431
|
+
|
|
1432
|
+
def destroy(self):
|
|
1433
|
+
if self.context is None:
|
|
1434
|
+
return
|
|
1435
|
+
self.deallocate()
|
|
1436
|
+
|
|
1437
|
+
def __enter__(self):
|
|
1438
|
+
if self.depth == 0:
|
|
1439
|
+
self.alloc()
|
|
1440
|
+
self.depth = self.depth + 1
|
|
1441
|
+
return self
|
|
1442
|
+
|
|
1443
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
1444
|
+
self.depth = self.depth - 1
|
|
1445
|
+
if self.depth < 0:
|
|
1446
|
+
raise Exception("Exit must not be called before enter, use the with keyword.")
|
|
1447
|
+
if self.depth == 0:
|
|
1448
|
+
self.destroy()
|
|
1449
|
+
return self
|
|
1450
|
+
|
|
1451
|
+
class eval_result:
|
|
1452
|
+
def __init__(self, wrapper, exception):
|
|
1453
|
+
self.wrapper = wrapper
|
|
1454
|
+
self.jsvalue = wrapper.jsvalue
|
|
1455
|
+
self.exception = exception
|
|
1456
|
+
|
|
1457
|
+
@property
|
|
1458
|
+
def value(self):
|
|
1459
|
+
return self.wrapper.value
|
|
1460
|
+
|
|
1461
|
+
def __repr__(self):
|
|
1462
|
+
return str({"value":self.value, "exception":self.exception})
|
|
1463
|
+
|
|
1464
|
+
def eval(self, script, sourceUrl=None):
|
|
1465
|
+
self.alloc()
|
|
1466
|
+
result, ex = jscore.context_eval(self.context, script, sourceUrl)
|
|
1467
|
+
result = self.eval_result(javascript_value(result), ex)
|
|
1468
|
+
return result
|
|
1469
|
+
|
|
1470
|
+
def eval_jsscript(self, jsscript):
|
|
1471
|
+
if jsscript is None:
|
|
1472
|
+
raise ValueError("Null JSScript pointer")
|
|
1473
|
+
result = self.context.evaluateJSScript(jsscript) # crashes on null or invalid script ptr
|
|
1474
|
+
result = ObjCInstance(result)
|
|
1475
|
+
ex = self.context.exception()
|
|
1476
|
+
if ex is not None:
|
|
1477
|
+
self.context.setException(None) # clear exception if set
|
|
1478
|
+
result = self.eval_result(javascript_value(result), ex)
|
|
1479
|
+
return result
|
|
1480
|
+
|
|
1481
|
+
def eval_script_source(self, source, scriptType = jscore.kJSScriptTypeProgram, modulePath = None):
|
|
1482
|
+
script = self.loader.load_source(source, scriptType, modulePath)
|
|
1483
|
+
return self.eval_jsscript(script)
|
|
1484
|
+
|
|
1485
|
+
def eval_script_file(self, path, scriptType = jscore.kJSScriptTypeProgram):
|
|
1486
|
+
script = self.loader.load_file(path, scriptType)
|
|
1487
|
+
return self.eval_jsscript(script)
|
|
1488
|
+
|
|
1489
|
+
def eval_source(self, source):
|
|
1490
|
+
return self.eval_script_source(source, jscore.kJSScriptTypeProgram)
|
|
1491
|
+
|
|
1492
|
+
def eval_file(self, path):
|
|
1493
|
+
return self.eval_script_file(path, jscore.kJSScriptTypeProgram)
|
|
1494
|
+
|
|
1495
|
+
def eval_module_source(self, source, modulePath = None):
|
|
1496
|
+
return self.eval_script_source(source, jscore.kJSScriptTypeModule, modulePath)
|
|
1497
|
+
|
|
1498
|
+
def eval_module_file(self, path):
|
|
1499
|
+
return self.eval_script_file(path, jscore.kJSScriptTypeModule)
|
|
1500
|
+
|
|
1501
|
+
@property
|
|
1502
|
+
def context_ref(self):
|
|
1503
|
+
self.alloc()
|
|
1504
|
+
return self.context.JSGlobalContextRef()
|
|
1505
|
+
|
|
1506
|
+
def callback(self, func, name = None):
|
|
1507
|
+
if not javascript_callback.is_callable(func):
|
|
1508
|
+
raise Exception(f"'{func}' is not a python callable/function")
|
|
1509
|
+
key = func
|
|
1510
|
+
callback = self.callbacks.get(key, None)
|
|
1511
|
+
if callback is None:
|
|
1512
|
+
callback = javascript_callback(func, name)
|
|
1513
|
+
self.callbacks[key] = callback
|
|
1514
|
+
return callback
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
class jscore_runtime:
|
|
1518
|
+
def __init__(self, vm = None):
|
|
1519
|
+
self.vm = vm
|
|
1520
|
+
self.vm_owner = self.vm is None
|
|
1521
|
+
self.depth = 0
|
|
1522
|
+
self.module_paths = {}
|
|
1523
|
+
self.scripts = []
|
|
1524
|
+
|
|
1525
|
+
def allocate(self):
|
|
1526
|
+
if self.vm is not None:
|
|
1527
|
+
raise Exception("VM already allocated. Do not call allocate/deallocate manually")
|
|
1528
|
+
self.vm = jscore.vm_allocate()
|
|
1529
|
+
self.vm_owner = True
|
|
1530
|
+
|
|
1531
|
+
def deallocate(self):
|
|
1532
|
+
if self.vm is None:
|
|
1533
|
+
raise Exception("VM already deallocated. Do not call allocate/deallocate manually")
|
|
1534
|
+
jscore.runtime_deallocate(self, self.vm_owner)
|
|
1535
|
+
self.vm = None
|
|
1536
|
+
self.scripts = []
|
|
1537
|
+
self.module_paths = {}
|
|
1538
|
+
|
|
1539
|
+
def alloc(self):
|
|
1540
|
+
if self.vm is not None:
|
|
1541
|
+
return
|
|
1542
|
+
self.allocate()
|
|
1543
|
+
|
|
1544
|
+
def destroy(self):
|
|
1545
|
+
if self.vm is None:
|
|
1546
|
+
return
|
|
1547
|
+
self.deallocate()
|
|
1548
|
+
|
|
1549
|
+
def get_module_path(self, source, modulePath = None):
|
|
1550
|
+
path = self.module_paths.get(source)
|
|
1551
|
+
if path is not None:
|
|
1552
|
+
return path
|
|
1553
|
+
if modulePath is not None:
|
|
1554
|
+
modulePath = self.get_file_path(modulePath)
|
|
1555
|
+
path = str(modulePath)
|
|
1556
|
+
if path is None:
|
|
1557
|
+
path = tempfile.mktemp(dir = Path.cwd())
|
|
1558
|
+
self.module_paths[source] = path
|
|
1559
|
+
return path
|
|
1560
|
+
|
|
1561
|
+
def get_file_path(self, path):
|
|
1562
|
+
path = str(path)
|
|
1563
|
+
if path.startswith("file://"):
|
|
1564
|
+
path = path[7:]
|
|
1565
|
+
p = Path(path)
|
|
1566
|
+
if not p.is_absolute():
|
|
1567
|
+
p = Path.cwd().joinpath(p)
|
|
1568
|
+
return p
|
|
1569
|
+
|
|
1570
|
+
def load_source(self, source, scriptType = jscore.kJSScriptTypeProgram, modulePath = None):
|
|
1571
|
+
loader = jscore.JSScript.scriptOfType_withSource_andSourceURL_andBytecodeCache_inVirtualMachine_error_
|
|
1572
|
+
sourceUrl = self.get_module_path(source, modulePath)
|
|
1573
|
+
return self.load_script(loader, source, scriptType, sourceUrl)
|
|
1574
|
+
|
|
1575
|
+
def load_file(self, path, scriptType = jscore.kJSScriptTypeProgram):
|
|
1576
|
+
p = self.get_file_path(path)
|
|
1577
|
+
sourceUrl = None
|
|
1578
|
+
if not p.is_file() or not p.exists():
|
|
1579
|
+
raise FileNotFoundError(f"Script file not found '{path}' ({p})")
|
|
1580
|
+
path = str(p)
|
|
1581
|
+
path = nsurl(path)
|
|
1582
|
+
sourceUrl = str(p)
|
|
1583
|
+
loader = jscore.JSScript.scriptOfType_memoryMappedFromASCIIFile_withSourceURL_andBytecodeCache_inVirtualMachine_error_
|
|
1584
|
+
return self.load_script(loader, path, scriptType, sourceUrl)
|
|
1585
|
+
|
|
1586
|
+
def load_script_ref(self, path = None, source = None, url = None):
|
|
1587
|
+
url = self.get_file_path(url)
|
|
1588
|
+
if source is None and path is not None:
|
|
1589
|
+
path = self.get_file_path(path)
|
|
1590
|
+
with open(path) as source_file:
|
|
1591
|
+
source = source_file.read()
|
|
1592
|
+
if source is None:
|
|
1593
|
+
raise ValueError("Source or source file path must be specified")
|
|
1594
|
+
url = self.get_module_path(source, url)
|
|
1595
|
+
url = f"file://{url}"
|
|
1596
|
+
script = jsscript_ref(self, url, source)
|
|
1597
|
+
self.scripts.append(script)
|
|
1598
|
+
return script
|
|
1599
|
+
|
|
1600
|
+
def load_script(self, loader, context, scriptType = jscore.kJSScriptTypeProgram, sourceUrl = None):
|
|
1601
|
+
if sourceUrl is None:
|
|
1602
|
+
raise ValueError("A valid source url is required")
|
|
1603
|
+
sourceUrl = nsurl(sourceUrl)
|
|
1604
|
+
error = c_void_p(None)
|
|
1605
|
+
#bytecodeCache requires a data vault if specified, so we don't...
|
|
1606
|
+
script = loader(scriptType, context, sourceUrl, None, self.vm, byref(error))
|
|
1607
|
+
retain_global(script) # DO NOT release JSScripts this will cause a crash while vm is alive
|
|
1608
|
+
if error:
|
|
1609
|
+
error = ObjCInstance(error)
|
|
1610
|
+
else:
|
|
1611
|
+
error = None
|
|
1612
|
+
sourceUrl = str(sourceUrl)
|
|
1613
|
+
self.scripts.append(script)
|
|
1614
|
+
return script, sourceUrl, error
|
|
1615
|
+
|
|
1616
|
+
def __enter__(self):
|
|
1617
|
+
if self.depth == 0:
|
|
1618
|
+
self.alloc()
|
|
1619
|
+
self.depth = self.depth + 1
|
|
1620
|
+
return self
|
|
1621
|
+
|
|
1622
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
1623
|
+
self.depth = self.depth - 1
|
|
1624
|
+
if self.depth < 0:
|
|
1625
|
+
raise Exception("Exit must not be called before enter, use the with keyword.")
|
|
1626
|
+
if self.depth == 0:
|
|
1627
|
+
self.destroy()
|
|
1628
|
+
return self
|
|
1629
|
+
|
|
1630
|
+
def new_context(self):
|
|
1631
|
+
raise NotImplementedError()
|
|
1632
|
+
|
|
1633
|
+
def context(self):
|
|
1634
|
+
self.alloc()
|
|
1635
|
+
return self.new_context()
|
|
1636
|
+
|
|
1637
|
+
# helper class to determine the minimum set of changes to build a jsvalue in terms of javascript statements
|
|
1638
|
+
class jsvalue_evaluator:
|
|
1639
|
+
def __init__(self, context, parent = None):
|
|
1640
|
+
self.context = context
|
|
1641
|
+
self.parent = parent
|
|
1642
|
+
|
|
1643
|
+
def object_equal(self, x, y):
|
|
1644
|
+
if isinstance(x, dict):
|
|
1645
|
+
for k,v in x.items():
|
|
1646
|
+
try:
|
|
1647
|
+
if not self.item_equal(v, y[k]):
|
|
1648
|
+
return False
|
|
1649
|
+
except:
|
|
1650
|
+
return False
|
|
1651
|
+
return True
|
|
1652
|
+
elif isinstance(x, list):
|
|
1653
|
+
for i in range(len(x)):
|
|
1654
|
+
v = x[i]
|
|
1655
|
+
try:
|
|
1656
|
+
if not self.item_equal(v, y[i]):
|
|
1657
|
+
return False
|
|
1658
|
+
except:
|
|
1659
|
+
return False
|
|
1660
|
+
return True
|
|
1661
|
+
return False
|
|
1662
|
+
|
|
1663
|
+
def value_equal(self, x, y):
|
|
1664
|
+
if x is y or x == y:
|
|
1665
|
+
return True
|
|
1666
|
+
return str(x) == str(y) # repr compare
|
|
1667
|
+
|
|
1668
|
+
def item_equal(self, x, y):
|
|
1669
|
+
if isinstance(x, list) or isinstance(x, dict):
|
|
1670
|
+
return self.object_equal(x, y)
|
|
1671
|
+
return self.value_equal(x, y)
|
|
1672
|
+
|
|
1673
|
+
def eval_set(self, context, parent, key, value, current, equal = None):
|
|
1674
|
+
if isinstance(value, list):
|
|
1675
|
+
if (equal is None and self.object_equal(value, current)) or equal:
|
|
1676
|
+
return
|
|
1677
|
+
if javascript_value.is_null_or_undefined(current) or len(current) == 0:
|
|
1678
|
+
value = javascript_callback.wrap(self.context, value, key)
|
|
1679
|
+
jsvalue = jscore.py_to_jsvalue(context, value, parent)
|
|
1680
|
+
parent.setValue_forProperty_(jsvalue, key)
|
|
1681
|
+
return jsvalue
|
|
1682
|
+
jsvalue = parent.valueForProperty_(key)
|
|
1683
|
+
for i in range(len(value)):
|
|
1684
|
+
k = str(i)
|
|
1685
|
+
v = value[i]
|
|
1686
|
+
c = javascript_value.undefined
|
|
1687
|
+
try:
|
|
1688
|
+
c = current[i]
|
|
1689
|
+
except:
|
|
1690
|
+
pass
|
|
1691
|
+
if not self.item_equal(v, c):
|
|
1692
|
+
self.eval_set(context, jsvalue, k, v, c, False)
|
|
1693
|
+
return jsvalue
|
|
1694
|
+
elif isinstance(value, dict):
|
|
1695
|
+
if (equal is None and self.object_equal(value, current)) or equal:
|
|
1696
|
+
return
|
|
1697
|
+
if javascript_value.is_null_or_undefined(current) or len(current) == 0:
|
|
1698
|
+
value = javascript_callback.wrap(self.context, value, key)
|
|
1699
|
+
jsvalue = jscore.py_to_jsvalue(context, value, parent)
|
|
1700
|
+
return jsvalue
|
|
1701
|
+
jsvalue = parent.valueForProperty_(key)
|
|
1702
|
+
for k,v in value.items():
|
|
1703
|
+
c = javascript_value.undefined
|
|
1704
|
+
try:
|
|
1705
|
+
c = current[k]
|
|
1706
|
+
except:
|
|
1707
|
+
pass
|
|
1708
|
+
if not self.item_equal(v, c):
|
|
1709
|
+
self.eval_set(context, jsvalue, k, v, c, False)
|
|
1710
|
+
return jsvalue
|
|
1711
|
+
else:
|
|
1712
|
+
if (equal is None and self.value_equal(value, current)) or equal:
|
|
1713
|
+
return
|
|
1714
|
+
value = javascript_callback.wrap(self.context, value, key)
|
|
1715
|
+
jsvalue = jscore.py_to_jsvalue(context, value, parent)
|
|
1716
|
+
parent.setValue_forProperty_(jsvalue, key)
|
|
1717
|
+
return jsvalue
|
|
1718
|
+
|
|
1719
|
+
def set(self, key, value, current):
|
|
1720
|
+
value = jsobject_accessor.unwrap(value)
|
|
1721
|
+
current = jsobject_accessor.unwrap(current)
|
|
1722
|
+
jsvalue = self.parent
|
|
1723
|
+
context = self.context.context
|
|
1724
|
+
if jsvalue is None:
|
|
1725
|
+
jsvalue = context.globalObject()
|
|
1726
|
+
val = self.eval_set(context, jsvalue, key, value, current)
|
|
1727
|
+
if val is not None:
|
|
1728
|
+
jsvalue.setValue_forProperty_(val, key)
|
|
1729
|
+
|
|
1730
|
+
def set_self(self, value, current):
|
|
1731
|
+
value = jsobject_accessor.unwrap(value)
|
|
1732
|
+
current = jsobject_accessor.unwrap(current)
|
|
1733
|
+
jsvalue = self.parent
|
|
1734
|
+
context = self.context.context
|
|
1735
|
+
if isinstance(value, list):
|
|
1736
|
+
for i in range(len(value)):
|
|
1737
|
+
k = str(i)
|
|
1738
|
+
v = value[i]
|
|
1739
|
+
c = javascript_value.undefined
|
|
1740
|
+
try:
|
|
1741
|
+
c = current[i]
|
|
1742
|
+
except:
|
|
1743
|
+
pass
|
|
1744
|
+
self.eval_set(context, jsvalue, k, v, c)
|
|
1745
|
+
elif isinstance(value, dict):
|
|
1746
|
+
for k,v in value.items():
|
|
1747
|
+
c = javascript_value.undefined
|
|
1748
|
+
try:
|
|
1749
|
+
c = current[k]
|
|
1750
|
+
except:
|
|
1751
|
+
pass
|
|
1752
|
+
self.eval_set(context, jsvalue, k, v, c)
|
|
1753
|
+
|
|
1754
|
+
|
|
1755
|
+
# metaclass to map jsobjects to appear analogous to regular python objects
|
|
1756
|
+
class jsobject_accessor:
|
|
1757
|
+
def __init__(self, context, jsobject, path):
|
|
1758
|
+
self.___context___ = context
|
|
1759
|
+
self.___jsobject___ = jsobject
|
|
1760
|
+
self.___evaluator___ = jsvalue_evaluator(context, jsobject)
|
|
1761
|
+
self.___path___ = path
|
|
1762
|
+
self.___init___ = True
|
|
1763
|
+
|
|
1764
|
+
def ___get___(self, key):
|
|
1765
|
+
key = str(key)
|
|
1766
|
+
if not self.___jsobject___.hasProperty_(key):
|
|
1767
|
+
return javascript_value.undefined
|
|
1768
|
+
value = self.___jsobject___.valueForProperty(key)
|
|
1769
|
+
if jscore.jsvalue_is_object(value):
|
|
1770
|
+
path = key
|
|
1771
|
+
if self.___jsobject___.isArray():
|
|
1772
|
+
path = "".join([self.___path___,'[', path,']'])
|
|
1773
|
+
else:
|
|
1774
|
+
path = ".".join([self.___path___, path])
|
|
1775
|
+
return jsobject_accessor(self.___context___, value, path)
|
|
1776
|
+
return jscore.jsvalue_to_py(value)
|
|
1777
|
+
|
|
1778
|
+
def ___set___(self, key, value):
|
|
1779
|
+
current = self.___get___(key)
|
|
1780
|
+
path = key
|
|
1781
|
+
if self.___jsobject___.isArray():
|
|
1782
|
+
path = "".join([self.___path___,'[', path,']'])
|
|
1783
|
+
else:
|
|
1784
|
+
path = ".".join([self.___path___, path])
|
|
1785
|
+
self.___evaluator___.set(key, value, current)
|
|
1786
|
+
|
|
1787
|
+
def __len__(self):
|
|
1788
|
+
return len(jscore.jsobject_get_keys(self.___jsobject___))
|
|
1789
|
+
|
|
1790
|
+
def __repr__(self):
|
|
1791
|
+
return str(jscore.jsvalue_to_py(self.___jsobject___))
|
|
1792
|
+
|
|
1793
|
+
def __getattr__(self, key):
|
|
1794
|
+
return self.___get___(key)
|
|
1795
|
+
|
|
1796
|
+
def __setattr__(self, key, value):
|
|
1797
|
+
if not self.__dict__.get("___init___", False):
|
|
1798
|
+
super().__setattr__(key, value)
|
|
1799
|
+
else:
|
|
1800
|
+
self.___set___(key, value)
|
|
1801
|
+
|
|
1802
|
+
def __contains__(self, key):
|
|
1803
|
+
value = self.___get___(key)
|
|
1804
|
+
return not javascript_value.is_undefined(value)
|
|
1805
|
+
|
|
1806
|
+
def __getitem__(self, key):
|
|
1807
|
+
value = self.___get___(key)
|
|
1808
|
+
if javascript_value.is_undefined(value):
|
|
1809
|
+
raise IndexError(f'{key} is undefined.')
|
|
1810
|
+
return value
|
|
1811
|
+
|
|
1812
|
+
def __setitem__(self, key, value):
|
|
1813
|
+
self.___set___(k, value)
|
|
1814
|
+
|
|
1815
|
+
@classmethod
|
|
1816
|
+
def unwrap(cls, value):
|
|
1817
|
+
if isinstance(value, cls):
|
|
1818
|
+
return jscore.jsvalue_to_py(value.___jsobject___)
|
|
1819
|
+
return value
|
|
1820
|
+
|
|
1821
|
+
|
|
1822
|
+
# metaclass to map the main global context to appear analogous to a regular python object
|
|
1823
|
+
class javascript_context_accessor:
|
|
1824
|
+
def __init__(self, context):
|
|
1825
|
+
self.___context___ = context
|
|
1826
|
+
self.___globalObject___ = context.context.globalObject()
|
|
1827
|
+
self.___evaluator___ = jsvalue_evaluator(context, self.___globalObject___)
|
|
1828
|
+
self.___init___ = True
|
|
1829
|
+
|
|
1830
|
+
def ___get___(self, key):
|
|
1831
|
+
value = javascript_value.undefined
|
|
1832
|
+
if not isinstance(key, str):
|
|
1833
|
+
return value
|
|
1834
|
+
if self.___globalObject___.hasProperty_(key):
|
|
1835
|
+
value = self.___globalObject___.valueForProperty(key)
|
|
1836
|
+
else:
|
|
1837
|
+
result = self.___context___.eval(f'{key};')
|
|
1838
|
+
value = result.jsvalue
|
|
1839
|
+
if jscore.jsvalue_is_object(value):
|
|
1840
|
+
return jsobject_accessor(self.___context___, value, key)
|
|
1841
|
+
return jscore.jsvalue_to_py(value)
|
|
1842
|
+
|
|
1843
|
+
def ___set___(self, key, value):
|
|
1844
|
+
jsvalue = None
|
|
1845
|
+
if not self.___globalObject___.hasProperty_(key):
|
|
1846
|
+
result = self.___context___.eval(f'{key};')
|
|
1847
|
+
jsvalue = result.jsvalue
|
|
1848
|
+
if jscore.jsvalue_is_object(jsvalue) and (isinstance(value, list) or isinstance(value, dict)):
|
|
1849
|
+
evaluator = jsvalue_evaluator(self.___context___, jsvalue)
|
|
1850
|
+
current = jscore.jsvalue_to_py(jsvalue)
|
|
1851
|
+
evaluator.set_self(value, current)
|
|
1852
|
+
return
|
|
1853
|
+
else:
|
|
1854
|
+
jsvalue = self.___globalObject___.valueForProperty(key)
|
|
1855
|
+
current = javascript_value.undefined
|
|
1856
|
+
if jsvalue is not None:
|
|
1857
|
+
current = jscore.jsvalue_to_py(jsvalue)
|
|
1858
|
+
self.___evaluator___.set(key, value, current)
|
|
1859
|
+
|
|
1860
|
+
|
|
1861
|
+
def __getattr__(self, key):
|
|
1862
|
+
return self.___get___(key)
|
|
1863
|
+
|
|
1864
|
+
def __setattr__(self, key, value):
|
|
1865
|
+
if not self.__dict__.get("___init___", False):
|
|
1866
|
+
super().__setattr__(key, value)
|
|
1867
|
+
else:
|
|
1868
|
+
self.___set___(key, value)
|
|
1869
|
+
|
|
1870
|
+
def __contains__(self, key):
|
|
1871
|
+
value = self.___get___(key)
|
|
1872
|
+
return not javascript_value.is_undefined(value)
|
|
1873
|
+
|
|
1874
|
+
def __getitem__(self, key):
|
|
1875
|
+
value = self.___get___(key)
|
|
1876
|
+
if javascript_value.is_undefined(value):
|
|
1877
|
+
raise IndexError(f"'{key}' is undefined.")
|
|
1878
|
+
return value
|
|
1879
|
+
|
|
1880
|
+
def __setitem__(self, key, value):
|
|
1881
|
+
return self.___set___(key, value)
|
|
1882
|
+
|
|
1883
|
+
# concrete runtimes and contexts
|
|
1884
|
+
|
|
1885
|
+
# javascript
|
|
1886
|
+
class javascript_context(jscore_context):
|
|
1887
|
+
def __init__(self, runtime):
|
|
1888
|
+
super().__init__(runtime)
|
|
1889
|
+
self.accessor = None
|
|
1890
|
+
|
|
1891
|
+
def allocate(self):
|
|
1892
|
+
super().allocate()
|
|
1893
|
+
self.accessor = javascript_context_accessor(self)
|
|
1894
|
+
|
|
1895
|
+
def deallocate(self):
|
|
1896
|
+
self.accessor = None
|
|
1897
|
+
super().deallocate()
|
|
1898
|
+
|
|
1899
|
+
@property
|
|
1900
|
+
def js(self):
|
|
1901
|
+
self.alloc()
|
|
1902
|
+
return self.accessor
|
|
1903
|
+
|
|
1904
|
+
|
|
1905
|
+
class javascript_runtime(jscore_runtime):
|
|
1906
|
+
def new_context(self):
|
|
1907
|
+
return javascript_context(self)
|
|
1908
|
+
|
|
1909
|
+
# wasm (WebAssembly)
|
|
1910
|
+
class wasm_namespace:
|
|
1911
|
+
def __init__(self, imports = None):
|
|
1912
|
+
if imports is None:
|
|
1913
|
+
imports = {}
|
|
1914
|
+
self.___imports___ = imports
|
|
1915
|
+
self.___init___ = True
|
|
1916
|
+
|
|
1917
|
+
def ___get___(self, key):
|
|
1918
|
+
value = self.___imports___.get(key, javascript_value.undefined)
|
|
1919
|
+
if javascript_value.is_undefined(value):
|
|
1920
|
+
value = {}
|
|
1921
|
+
self.___set___(key, value)
|
|
1922
|
+
if isinstance(value, dict):
|
|
1923
|
+
return wasm_namespace(value)
|
|
1924
|
+
return value
|
|
1925
|
+
|
|
1926
|
+
def ___set___(self, key, value):
|
|
1927
|
+
self.___imports___[key] = value
|
|
1928
|
+
|
|
1929
|
+
def __getattr__(self, key):
|
|
1930
|
+
return self.___get___(key)
|
|
1931
|
+
|
|
1932
|
+
def __setattr__(self, key, value):
|
|
1933
|
+
if not self.__dict__.get("___init___", False):
|
|
1934
|
+
super().__setattr__(key, value)
|
|
1935
|
+
else:
|
|
1936
|
+
self.___set___(key, value)
|
|
1937
|
+
|
|
1938
|
+
def __contains__(self, key):
|
|
1939
|
+
value = self.___get___(key)
|
|
1940
|
+
return not javascript_value.is_undefined(value)
|
|
1941
|
+
|
|
1942
|
+
def __getitem__(self, key):
|
|
1943
|
+
value = self.___get___(key)
|
|
1944
|
+
if javascript_value.is_undefined(value):
|
|
1945
|
+
raise IndexError(f"'{key}' is undefined.")
|
|
1946
|
+
return value
|
|
1947
|
+
|
|
1948
|
+
def __setitem__(self, key, value):
|
|
1949
|
+
return self.___set___(key, value)
|
|
1950
|
+
|
|
1951
|
+
def __repr__(self):
|
|
1952
|
+
return str(self.___imports___)
|
|
1953
|
+
|
|
1954
|
+
class wasm_module:
|
|
1955
|
+
magic = b'\0asm'
|
|
1956
|
+
version = b'\1\0\0\0'
|
|
1957
|
+
header = magic + version
|
|
1958
|
+
|
|
1959
|
+
@classmethod
|
|
1960
|
+
def has_header(cls, data):
|
|
1961
|
+
header = cls.header
|
|
1962
|
+
header_len = len(header)
|
|
1963
|
+
index = 0
|
|
1964
|
+
for byte in data:
|
|
1965
|
+
if byte != header[index]:
|
|
1966
|
+
return False
|
|
1967
|
+
index = index + 1
|
|
1968
|
+
if index == header_len:
|
|
1969
|
+
return True
|
|
1970
|
+
return False
|
|
1971
|
+
|
|
1972
|
+
def __init__(self, data = None, name = None, imports = {}):
|
|
1973
|
+
self.name = name
|
|
1974
|
+
if self.name is not None:
|
|
1975
|
+
self.name = wasm_module.get_module_name(self.name)
|
|
1976
|
+
self.data = None
|
|
1977
|
+
self.nsdata = None
|
|
1978
|
+
self.context = None
|
|
1979
|
+
self.jsdata = None
|
|
1980
|
+
self._imports = imports
|
|
1981
|
+
self._namespace = wasm_namespace(self._imports)
|
|
1982
|
+
self.module = None
|
|
1983
|
+
self.instance = None
|
|
1984
|
+
if objc.ns_subclass_of(data, NSData):
|
|
1985
|
+
self.nsdata = data
|
|
1986
|
+
elif isinstance(data, list) or isinstance(data, bytes):
|
|
1987
|
+
self.data = []
|
|
1988
|
+
data = bytes(data)
|
|
1989
|
+
if not wasm_module.has_header(data):
|
|
1990
|
+
raise ArgumentError(f"Invalid wasm module. Modules must start with '{wasm_module.header}'.")
|
|
1991
|
+
self.data.append(data)
|
|
1992
|
+
elif isinstance(data, str) or isinstance(data, Path):
|
|
1993
|
+
self.nsdata = objc.nsdata_from_file(data)
|
|
1994
|
+
elif data is not None:
|
|
1995
|
+
raise ArgumentError("Unknown module data type "+type(data))
|
|
1996
|
+
else:
|
|
1997
|
+
self.data = []
|
|
1998
|
+
|
|
1999
|
+
def append(self, data):
|
|
2000
|
+
if self.data is None or self.nsdata is not None:
|
|
2001
|
+
raise Exception("NSData is read only")
|
|
2002
|
+
self.data.append(b''.join(data))
|
|
2003
|
+
|
|
2004
|
+
@property
|
|
2005
|
+
def bytes(self):
|
|
2006
|
+
if self.data is not None:
|
|
2007
|
+
bytes = b''.join(self.data)
|
|
2008
|
+
if not wasm_module.has_header(bytes):
|
|
2009
|
+
return wasm_module.header + bytes
|
|
2010
|
+
return bytes
|
|
2011
|
+
if self.nsdata is not None:
|
|
2012
|
+
return nsdata_to_bytes(self.nsdata)
|
|
2013
|
+
return b''
|
|
2014
|
+
|
|
2015
|
+
def load(self, context):
|
|
2016
|
+
if self.module is not None:
|
|
2017
|
+
return self.instance
|
|
2018
|
+
if self.nsdata is None and self.data is not None:
|
|
2019
|
+
self.nsdata = ns(self.bytes)
|
|
2020
|
+
self.data = None
|
|
2021
|
+
if self.nsdata is None:
|
|
2022
|
+
raise ImportError("Assembly data not loaded.")
|
|
2023
|
+
self.context = context
|
|
2024
|
+
bytes_len = self.nsdata.length()
|
|
2025
|
+
# Work around for MakeTypedArray returning NaN floats when wrapped in a JSValue
|
|
2026
|
+
self.jsdata = self.context.eval(f"new Uint8Array({bytes_len});").jsvalue
|
|
2027
|
+
context_ref, value_ref = jscore.jsvalue_get_refs(self.jsdata)
|
|
2028
|
+
ex = c_void_p(None)
|
|
2029
|
+
bytes_ptr = jscore.JSObjectGetTypedArrayBytesPtr(context_ref, value_ref, byref(ex))
|
|
2030
|
+
if bytes_ptr is None and ex.value is not None:
|
|
2031
|
+
raise ImportError(jscore.jsvalueref_to_py(context_ref, ex))
|
|
2032
|
+
# read nsdata directly into Uint8Array backing bytes
|
|
2033
|
+
self.nsdata.getBytes_length_(bytes_ptr, bytes_len)
|
|
2034
|
+
self.module, self.name = self.context._load_module_array(self.jsdata, self.name, self._imports)
|
|
2035
|
+
self.instance = self.module.instance
|
|
2036
|
+
return self.instance
|
|
2037
|
+
|
|
2038
|
+
@property
|
|
2039
|
+
def loaded(self):
|
|
2040
|
+
return self.instance is not None
|
|
2041
|
+
|
|
2042
|
+
@property
|
|
2043
|
+
def imports(self):
|
|
2044
|
+
return self._namespace
|
|
2045
|
+
|
|
2046
|
+
@property
|
|
2047
|
+
def exports(self):
|
|
2048
|
+
if not self.loaded:
|
|
2049
|
+
return {}
|
|
2050
|
+
return self.instance.exports
|
|
2051
|
+
|
|
2052
|
+
def free(self):
|
|
2053
|
+
self.data = None
|
|
2054
|
+
self.nsdata = None
|
|
2055
|
+
self.context = None
|
|
2056
|
+
self.jsdata = None
|
|
2057
|
+
self.module = None
|
|
2058
|
+
self.instance = None
|
|
2059
|
+
|
|
2060
|
+
def save(self, path):
|
|
2061
|
+
path = Path(str(path))
|
|
2062
|
+
if not path.is_absolute():
|
|
2063
|
+
path = path.cwd().joinpath(path)
|
|
2064
|
+
with open(path, "w") as module_file:
|
|
2065
|
+
module_file.write(self.bytes)
|
|
2066
|
+
|
|
2067
|
+
@classmethod
|
|
2068
|
+
def get_module_name(cls, path):
|
|
2069
|
+
path = str(path)
|
|
2070
|
+
if '/' not in path and '.' not in path:
|
|
2071
|
+
return path
|
|
2072
|
+
name = Path(str(path)).name.split('.wasm')[0]
|
|
2073
|
+
if '.' in name:
|
|
2074
|
+
name = name.split('.')[0]
|
|
2075
|
+
return name
|
|
2076
|
+
|
|
2077
|
+
@classmethod
|
|
2078
|
+
def from_file_py(cls, path):
|
|
2079
|
+
path = Path(str(path))
|
|
2080
|
+
if not path.is_absolute():
|
|
2081
|
+
path = path.cwd().joinpath(path)
|
|
2082
|
+
with open(path) as module_file:
|
|
2083
|
+
data = module_file.read()
|
|
2084
|
+
name = cls.get_module_name(path)
|
|
2085
|
+
return cls(data, name)
|
|
2086
|
+
|
|
2087
|
+
@classmethod
|
|
2088
|
+
def from_file(cls, path, fileManager = None):
|
|
2089
|
+
data = objc.nsdata_from_file(path, fileManager)
|
|
2090
|
+
name = cls.get_module_name(path)
|
|
2091
|
+
return cls(data, name)
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
class wasm_context(jscore_context):
|
|
2095
|
+
def __init__(self, runtime):
|
|
2096
|
+
super().__init__(runtime)
|
|
2097
|
+
self._modules = {}
|
|
2098
|
+
self._imports = {}
|
|
2099
|
+
self._namespace = wasm_namespace(self._imports)
|
|
2100
|
+
|
|
2101
|
+
def allocate(self):
|
|
2102
|
+
super().allocate()
|
|
2103
|
+
self._load_module = self.eval("""
|
|
2104
|
+
const _jscore_wasm_modules = {}
|
|
2105
|
+
function _jscore_wasm_load(name, wasm_bin, namespace){
|
|
2106
|
+
if(namespace === null) { namespace = {}; }
|
|
2107
|
+
const wasm_module = new WebAssembly.Module(wasm_bin);
|
|
2108
|
+
const wasm_instance = new WebAssembly.Instance(wasm_module, namespace);
|
|
2109
|
+
const wasm_module_instance = {"instance": wasm_instance, "namespace": namespace, "module": wasm_module};
|
|
2110
|
+
_jscore_wasm_modules[name] = wasm_module_instance; // ensure module remains in scope
|
|
2111
|
+
return wasm_module_instance;
|
|
2112
|
+
};_jscore_wasm_load;""").value
|
|
2113
|
+
|
|
2114
|
+
def deallocate(self):
|
|
2115
|
+
for name,module in self._modules.items():
|
|
2116
|
+
module.free()
|
|
2117
|
+
self._modules = None
|
|
2118
|
+
super().deallocate()
|
|
2119
|
+
|
|
2120
|
+
@property
|
|
2121
|
+
def imports(self):
|
|
2122
|
+
return self._namespace
|
|
2123
|
+
|
|
2124
|
+
@property
|
|
2125
|
+
def modules(self):
|
|
2126
|
+
return dict(self._modules)
|
|
2127
|
+
|
|
2128
|
+
def module(self, name):
|
|
2129
|
+
name = wasm_module.get_module_name(name)
|
|
2130
|
+
return self._modules.get(name)
|
|
2131
|
+
|
|
2132
|
+
def module_instance(self, name):
|
|
2133
|
+
module = self.module(name)
|
|
2134
|
+
if module is None:
|
|
2135
|
+
return None
|
|
2136
|
+
return module.instance
|
|
2137
|
+
|
|
2138
|
+
def load_module(self, module):
|
|
2139
|
+
if isinstance(module, Path):
|
|
2140
|
+
module = wasm_module.from_file(module)
|
|
2141
|
+
if not isinstance(module, wasm_module):
|
|
2142
|
+
raise ArgumentError("Module must be wasm_module")
|
|
2143
|
+
result = self._modules.get(module.name)
|
|
2144
|
+
if result is not None:
|
|
2145
|
+
return result
|
|
2146
|
+
result = module.load(self)
|
|
2147
|
+
self._modules[module.name] = module
|
|
2148
|
+
return result
|
|
2149
|
+
|
|
2150
|
+
def _create_imports_namespace(self, imports = None):
|
|
2151
|
+
namespace = {}
|
|
2152
|
+
for k, v in self._imports.items():
|
|
2153
|
+
namespace[k] = v
|
|
2154
|
+
if imports is None:
|
|
2155
|
+
imports = {}
|
|
2156
|
+
for k, v in imports.items():
|
|
2157
|
+
namespace[k] = v
|
|
2158
|
+
if len(namespace) == 0:
|
|
2159
|
+
return None
|
|
2160
|
+
namespace = javascript_callback.wrap(self, namespace)
|
|
2161
|
+
jsnamespace = jscore.py_to_jsvalue(self.context, namespace)
|
|
2162
|
+
return jsnamespace
|
|
2163
|
+
|
|
2164
|
+
def _add_module_to_global_namespace(self, module, name): #?
|
|
2165
|
+
pass
|
|
2166
|
+
|
|
2167
|
+
def _load_module_array(self, module_data, name = None, imports = None):
|
|
2168
|
+
if not jscore.jsvalue_is_array_type(module_data, jscore.kJSTypedArrayTypeUint8Array):
|
|
2169
|
+
raise ArgumentError("Module array must be JSValue of an Uint8Array instance type.")
|
|
2170
|
+
if name is None:
|
|
2171
|
+
name = "wasm_module_"+str(len(self._modules))
|
|
2172
|
+
namespace = self._create_imports_namespace(imports)
|
|
2173
|
+
result = self._load_module(name, module_data, namespace)
|
|
2174
|
+
self._add_module_to_global_namespace(result, name)
|
|
2175
|
+
return result, name
|
|
2176
|
+
|
|
2177
|
+
|
|
2178
|
+
class wasm_runtime(jscore_runtime):
|
|
2179
|
+
def new_context(self):
|
|
2180
|
+
return wasm_context(self)
|
|
2181
|
+
|
|
2182
|
+
|
|
2183
|
+
if __name__ == '__main__':
|
|
2184
|
+
import console
|
|
2185
|
+
|
|
2186
|
+
console.clear()
|
|
2187
|
+
|
|
2188
|
+
runtime = jscore.runtime()
|
|
2189
|
+
context = runtime.context()
|
|
2190
|
+
expected_unset = object()
|
|
2191
|
+
|
|
2192
|
+
valueMatch = None
|
|
2193
|
+
arrayMatch = None
|
|
2194
|
+
objectMatch = None
|
|
2195
|
+
|
|
2196
|
+
def valueMatch(expected, value, values = {}, repr = False):
|
|
2197
|
+
if expected is None:
|
|
2198
|
+
return value is None
|
|
2199
|
+
if expected is javascript_value.undefined:
|
|
2200
|
+
return value is javascript_value.undefined
|
|
2201
|
+
if isinstance(expected, dict):
|
|
2202
|
+
if not isinstance(value, dict):
|
|
2203
|
+
return False
|
|
2204
|
+
return objectMatch(expected, value)
|
|
2205
|
+
elif isinstance(expected, list):
|
|
2206
|
+
if not isinstance(value, list):
|
|
2207
|
+
return False
|
|
2208
|
+
return arrayMatch(expected, value)
|
|
2209
|
+
elif expected is not value and expected != value:
|
|
2210
|
+
if repr and not isinstance(value,str):
|
|
2211
|
+
return expected == str(value)
|
|
2212
|
+
if callable(expected):
|
|
2213
|
+
expected = expected()
|
|
2214
|
+
values["expected"] = expected
|
|
2215
|
+
return valueMatch(expected, value, repr, values)
|
|
2216
|
+
if callable(value):
|
|
2217
|
+
value = value()
|
|
2218
|
+
values["value"] = value
|
|
2219
|
+
return valueMatch(expected, value, repr, values)
|
|
2220
|
+
return False
|
|
2221
|
+
return True
|
|
2222
|
+
|
|
2223
|
+
def arrayMatch(expected, value):
|
|
2224
|
+
if expected is None:
|
|
2225
|
+
return value is None
|
|
2226
|
+
if expected is javascript_value.undefined:
|
|
2227
|
+
return value is javascript_value.undefined
|
|
2228
|
+
if not isinstance(expected, list) or not isinstance(value, list):
|
|
2229
|
+
return False
|
|
2230
|
+
if len(expected) != len(value):
|
|
2231
|
+
return False
|
|
2232
|
+
for i in range(len(value)):
|
|
2233
|
+
e = expected[i]
|
|
2234
|
+
v = value[i]
|
|
2235
|
+
if not valueMatch(expected[i], value[i]):
|
|
2236
|
+
return False
|
|
2237
|
+
return True
|
|
2238
|
+
|
|
2239
|
+
def objectMatch(expected, value):
|
|
2240
|
+
if expected is None:
|
|
2241
|
+
return value is None
|
|
2242
|
+
if expected is javascript_value.undefined:
|
|
2243
|
+
return value is javascript_value.undefined
|
|
2244
|
+
if not isinstance(expected, dict) or not isinstance(value, dict):
|
|
2245
|
+
return False
|
|
2246
|
+
for k, v in expected.items():
|
|
2247
|
+
if not k in value:
|
|
2248
|
+
return False
|
|
2249
|
+
vv = value[k]
|
|
2250
|
+
if not valueMatch(v, vv):
|
|
2251
|
+
return False
|
|
2252
|
+
return True
|
|
2253
|
+
|
|
2254
|
+
def eval(script, expected=expected_unset, **kwargs):
|
|
2255
|
+
print(f'Execute:\n{script}\n')
|
|
2256
|
+
result = context.eval(script)
|
|
2257
|
+
value = result.value
|
|
2258
|
+
ex = result.exception
|
|
2259
|
+
print(f'Result:\n{value}\n')
|
|
2260
|
+
if not ex is None:
|
|
2261
|
+
print(f'Exception:\n{ex}')
|
|
2262
|
+
if expected is not expected_unset:
|
|
2263
|
+
values = {"expected":expected, "value":value}
|
|
2264
|
+
match = valueMatch(expected, value, values=values, **kwargs)
|
|
2265
|
+
expected = values["expected"]
|
|
2266
|
+
value = values["value"]
|
|
2267
|
+
print(f"Expected: {expected}\nActual: {value}\nPassed: {match}")
|
|
2268
|
+
print("-" * 35)
|
|
2269
|
+
return value
|
|
2270
|
+
|
|
2271
|
+
def header(text, end_only = False):
|
|
2272
|
+
if not end_only:
|
|
2273
|
+
print("")
|
|
2274
|
+
print("-" * 35)
|
|
2275
|
+
print(text)
|
|
2276
|
+
print("-" * 35)
|
|
2277
|
+
print("")
|
|
2278
|
+
|
|
2279
|
+
header("javascript runtime")
|
|
2280
|
+
print(runtime, context)
|
|
2281
|
+
header("primitives", True)
|
|
2282
|
+
eval("parseInt('1')", 1)
|
|
2283
|
+
|
|
2284
|
+
eval("1+1", 2)
|
|
2285
|
+
|
|
2286
|
+
eval("parseFloat('1.20')", 1.2)
|
|
2287
|
+
|
|
2288
|
+
eval("1.1 + 1.1", 2.2)
|
|
2289
|
+
|
|
2290
|
+
eval("1.02", 1.02)
|
|
2291
|
+
|
|
2292
|
+
eval("false", False)
|
|
2293
|
+
|
|
2294
|
+
eval("true", True)
|
|
2295
|
+
|
|
2296
|
+
eval("'c'", 'c')
|
|
2297
|
+
|
|
2298
|
+
header("strings")
|
|
2299
|
+
|
|
2300
|
+
eval('"string"', "string")
|
|
2301
|
+
|
|
2302
|
+
header("datetimes")
|
|
2303
|
+
|
|
2304
|
+
eval("new Date()")
|
|
2305
|
+
|
|
2306
|
+
header("exceptions")
|
|
2307
|
+
eval('throw "errooorrr";')
|
|
2308
|
+
|
|
2309
|
+
header("arrays")
|
|
2310
|
+
eval("[]", [])
|
|
2311
|
+
|
|
2312
|
+
eval("[ 1, 2 , 3 ]", [ 1, 2, 3 ])
|
|
2313
|
+
|
|
2314
|
+
eval("[ true, false, true, false ]", [ True, False, True, False ])
|
|
2315
|
+
|
|
2316
|
+
eval("[ 'a', 'b', 'c' ]", [ 'a', 'b', 'c' ])
|
|
2317
|
+
|
|
2318
|
+
eval('[ "abc" , "def", "ghi" ]', [ "abc" , "def", "ghi" ])
|
|
2319
|
+
|
|
2320
|
+
eval('[ [1,"2"], ["a"], [{"1":2, "obj":{}, "arr":[]}]]', [ [1,"2"], ["a"], [ {"1":2, "obj":{}, "arr":[] }]])
|
|
2321
|
+
|
|
2322
|
+
header("objects")
|
|
2323
|
+
eval('const obj = { "str": "str", "int": 1, "float": 1.4, "obj":{ "hello": "world"} }; obj;', {"str":"str", "int":1, "float": 1.4, "obj": {"hello": "world"}})
|
|
2324
|
+
|
|
2325
|
+
eval('const fn = function() { return 10; }; fn;', 10)
|
|
2326
|
+
|
|
2327
|
+
eval('const fnobj = { "fn": function() { return 10; }}; fnobj;', {"fn": 10})
|
|
2328
|
+
|
|
2329
|
+
header("wasm")
|
|
2330
|
+
#instantiate empty module
|
|
2331
|
+
eval('''(function(){
|
|
2332
|
+
const bin = new Uint8Array([0,97,115,109,1,0,0,0]);
|
|
2333
|
+
let result = null;
|
|
2334
|
+
try
|
|
2335
|
+
{
|
|
2336
|
+
const module = new WebAssembly.Module(bin);
|
|
2337
|
+
const instance = new WebAssembly.Instance(module);
|
|
2338
|
+
result = ''+module+' '+instance;
|
|
2339
|
+
}
|
|
2340
|
+
catch(ex)
|
|
2341
|
+
{
|
|
2342
|
+
result = ''+ex;
|
|
2343
|
+
}
|
|
2344
|
+
return result;
|
|
2345
|
+
})();
|
|
2346
|
+
//
|
|
2347
|
+
''')
|
|
2348
|
+
# A memset test as described:
|
|
2349
|
+
# https://developer.apple.com/forums/thread/121040
|
|
2350
|
+
inst = eval('''(function(){
|
|
2351
|
+
const bin = new Uint8Array([0,97,115,109,1,0,0,0,1,6,1,96,1,127,1,127,3,2,1,0,5,3,1,0,1,7,8,1,4,116,101,115,116,0,0,10,16,1,14,0,32,0,65,1,54,2,0,32,0,40,2,0,11]);
|
|
2352
|
+
let result = null;
|
|
2353
|
+
try
|
|
2354
|
+
{
|
|
2355
|
+
const module = new WebAssembly.Module(bin);
|
|
2356
|
+
const instance = new WebAssembly.Instance(module);
|
|
2357
|
+
result = ''+module+' '+instance;
|
|
2358
|
+
result += '\\n'+instance.exports.test(4);
|
|
2359
|
+
return instance;
|
|
2360
|
+
}
|
|
2361
|
+
catch(ex)
|
|
2362
|
+
{
|
|
2363
|
+
result = ''+ex;
|
|
2364
|
+
}
|
|
2365
|
+
return result;
|
|
2366
|
+
})();
|
|
2367
|
+
//
|
|
2368
|
+
''')
|
|
2369
|
+
|
|
2370
|
+
print(inst.exports.test.is_native)
|
|
2371
|
+
|
|
2372
|
+
header("context.js interop")
|
|
2373
|
+
header("Create new object", True)
|
|
2374
|
+
print("context.js.interop_obj = { 'test':{'object':[]}, 'int':1, 'double':2.45 }")
|
|
2375
|
+
context.js.interop_obj = { 'test':{'object':[]}, 'int':1, 'double':2.45 }
|
|
2376
|
+
print("Result:", context.js.interop_obj)
|
|
2377
|
+
|
|
2378
|
+
header("Modify object")
|
|
2379
|
+
print("context.js.interop_obj = { 'test':{'object':[1,2,3]}, 'int':1, 'double':2.45 }")
|
|
2380
|
+
context.js.interop_obj = { 'test':{'object':[1,2,3]}, 'int':1, 'double':2.45 }
|
|
2381
|
+
print("Result:", context.js.interop_obj)
|
|
2382
|
+
|
|
2383
|
+
header("Create new function")
|
|
2384
|
+
print('"interopfn" in context.js = ', "interopfn" in context.js)
|
|
2385
|
+
print('context.js.interopfn = javascript_function.from_body("function() { return 20; }")')
|
|
2386
|
+
context.js.interopfn = javascript_function.from_source("function() { return 20; }")
|
|
2387
|
+
print('"interopfn" in context.js = ', "interopfn" in context.js)
|
|
2388
|
+
print("Result:",context.js.interopfn, context.js.interopfn())
|
|
2389
|
+
|
|
2390
|
+
header("Define/Load function")
|
|
2391
|
+
print('"fndeftest" in context.js = ', "fndeftest" in context.js)
|
|
2392
|
+
print('context.eval("function fndeftest() { return 123; }")')
|
|
2393
|
+
context.eval("function fndeftest() { return 123; }")
|
|
2394
|
+
print('"fndeftest" in context.js = ', "fndeftest" in context.js)
|
|
2395
|
+
print("Result:", context.js.fndeftest, context.js.fndeftest())
|
|
2396
|
+
|
|
2397
|
+
header("Define python functions callable from js")
|
|
2398
|
+
context.js.pythonfn = lambda text: print(text)
|
|
2399
|
+
print("context.js.pythonfn = lambda text: print(text)")
|
|
2400
|
+
print(context.js.pythonfn)
|
|
2401
|
+
print("context.eval('pythonfn(\"Hello python\");')")
|
|
2402
|
+
context.eval('pythonfn("Hello python");')
|
|
2403
|
+
print()
|
|
2404
|
+
context.js.python_val = lambda: {"str": "Hello from python", "num":10, "list":[1,2,3]}
|
|
2405
|
+
print('context.js.python_val = lambda: {"str": "Hello from python", "num":10, "list":[1,2,3]}')
|
|
2406
|
+
print(context.js.python_val)
|
|
2407
|
+
print("context.eval('python_val();')")
|
|
2408
|
+
print(context.eval('python_val();'))
|
|
2409
|
+
|
|
2410
|
+
header("Modules/scripts")
|
|
2411
|
+
context.eval_module_source("function module_source() { return 10; }", "sourcetest.js")
|
|
2412
|
+
print(context.js.module_source)
|
|
2413
|
+
#context.eval_module_file("./test.js")
|
|
2414
|
+
#print(context.js.filetest)
|
|
2415
|
+
#str_ref = jscore.str_to_jsstringref("test test")
|
|
2416
|
+
#print(str_ref)
|
|
2417
|
+
#py_str = jscore.jsstringref_to_py(str_ref)
|
|
2418
|
+
#print(py_str)
|
|
2419
|
+
script_ref = runtime.load_script_ref(source="function script_ref(){ return 232; }; [1, '2', new Date(), script_ref, {'a':[]}];" , url="reftest.js")
|
|
2420
|
+
value, exception = script_ref.eval(context)
|
|
2421
|
+
print(value, exception, context.js.script_ref)
|
|
2422
|
+
context.destroy()
|
|
2423
|
+
runtime.destroy()
|
|
2424
|
+
print(jscore._runtimes)
|
|
2425
|
+
|
|
2426
|
+
header("wasm runtime")
|
|
2427
|
+
runtime = jscore.runtime(wasm_runtime)
|
|
2428
|
+
context = runtime.context()
|
|
2429
|
+
print(runtime, context)
|
|
2430
|
+
|
|
2431
|
+
module = wasm_module([0,97,115,109,1,0,0,0,1,6,1,96,1,127,1,127,3,2,1,0,5,3,1,0,1,7,8,1,4,116,101,115,116,0,0,10,16,1,14,0,32,0,65,1,54,2,0,32,0,40,2,0,11])
|
|
2432
|
+
context.load_module(module)
|
|
2433
|
+
print(module.exports)
|
|
2434
|
+
|
|
2435
|
+
#https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/Using_the_JavaScript_API
|
|
2436
|
+
simple_module_path = Path("./simple.wasm")
|
|
2437
|
+
if simple_module_path.exists():
|
|
2438
|
+
header("simple.wasm")
|
|
2439
|
+
simple_module = wasm_module.from_file("./simple.wasm")
|
|
2440
|
+
simple_module.imports.my_namespace.imported_func = lambda *v: print(*v)
|
|
2441
|
+
context.load_module(simple_module)
|
|
2442
|
+
print(simple_module.exports)
|
|
2443
|
+
simple_module.exports.exported_func()
|
|
2444
|
+
|
|
2445
|
+
context.destroy()
|
|
2446
|
+
runtime.destroy()
|
|
2447
|
+
print(jscore._runtimes)
|
|
2448
|
+
print(jscore._runtime_vm)
|