Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/py_mini_racer/_abstract_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ def array_insert(
) -> None:
pass

@abstractmethod
def array_push(self, arr: JSArray, new_val: PythonJSConvertedTypes) -> None:
pass

@abstractmethod
def call_function(
self,
Expand Down
9 changes: 9 additions & 0 deletions src/py_mini_racer/_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,15 @@ def array_insert(
)
).to_python_or_raise()

def array_push(self, arr: JSArray, new_val: PythonJSConvertedTypes) -> None:
arr_handle = python_to_value_handle(self, arr)
new_val_handle = python_to_value_handle(self, new_val)

# Convert the value just to convert any exceptions (and GC the result)
self._wrap_raw_handle(
self._get_dll().mr_array_push(self._ctx, arr_handle.raw, new_val_handle.raw)
).to_python_or_raise()

def call_function(
self,
func: JSFunction,
Expand Down
3 changes: 3 additions & 0 deletions src/py_mini_racer/_dll.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ def _build_dll_handle(dll_path: Path) -> ctypes.CDLL: # noqa: PLR0915
]
handle.mr_splice_array.restype = RawValueHandle

handle.mr_array_push.argtypes = [ctypes.c_uint64, RawValueHandle, RawValueHandle]
handle.mr_array_push.restype = RawValueHandle

handle.mr_call_function.argtypes = [
ctypes.c_uint64,
RawValueHandle,
Expand Down
5 changes: 4 additions & 1 deletion src/py_mini_racer/_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ def insert(self, index: int, new_obj: PythonJSConvertedTypes) -> None:

def __iter__(self) -> Iterator[PythonJSConvertedTypes]:
for i in range(len(self)):
yield self[i]
yield self._ctx.get_object_item(self, i)

def append(self, value: PythonJSConvertedTypes) -> None:
self._ctx.array_push(self, value)


class JSFunctionImpl(JSMappedObjectImpl, JSFunction):
Expand Down
23 changes: 23 additions & 0 deletions src/v8_py_frontend/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,29 @@ auto Context::SpliceArray(BinaryValueHandle* obj_handle,
.get());
}

auto Context::ArrayPush(BinaryValueHandle* obj_handle,
BinaryValueHandle* new_val_handle)
-> BinaryValueHandle* {
auto obj_hc = MakeHandleConverter(obj_handle, "Bad handle: obj");
if (!obj_hc) {
return obj_hc.GetErrorHandle();
}

auto new_val_hc = MakeHandleConverter(new_val_handle, "Bad handle: new_val");
if (!new_val_hc) {
return new_val_hc.GetErrorHandle();
}

return bv_registry_.Remember(
isolate_manager_
.Run([this, obj_ptr = obj_hc.GetPtr(),
new_val_ptr = new_val_hc.GetPtr()](v8::Isolate* isolate) {
return object_manipulator_.Push(isolate, obj_ptr.get(),
new_val_ptr.get());
})
.get());
}

void Context::FreeBinaryValue(BinaryValueHandle* val) {
bv_registry_.Forget(val);
}
Expand Down
2 changes: 2 additions & 0 deletions src/v8_py_frontend/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class Context {
int32_t start,
int32_t delete_count,
BinaryValueHandle* new_val_handle) -> BinaryValueHandle*;
auto ArrayPush(BinaryValueHandle* obj_handle,
BinaryValueHandle* new_val_handle) -> BinaryValueHandle*;
auto CallFunction(BinaryValueHandle* func_handle,
BinaryValueHandle* this_handle,
BinaryValueHandle* argv_handle,
Expand Down
11 changes: 11 additions & 0 deletions src/v8_py_frontend/exports.cc
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,17 @@ LIB_EXPORT auto mr_splice_array(uint64_t context_id,
new_val_handle);
}

LIB_EXPORT auto mr_array_push(uint64_t context_id,
MiniRacer::BinaryValueHandle* array_handle,
MiniRacer::BinaryValueHandle* new_val_handle)
-> MiniRacer::BinaryValueHandle* {
auto context = GetContext(context_id);
if (!context) {
return nullptr;
}
return context->ArrayPush(array_handle, new_val_handle);
}

LIB_EXPORT auto mr_call_function(uint64_t context_id,
MiniRacer::BinaryValueHandle* func_handle,
MiniRacer::BinaryValueHandle* this_handle,
Expand Down
10 changes: 10 additions & 0 deletions src/v8_py_frontend/exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@ LIB_EXPORT auto mr_splice_array(uint64_t context_id,
MiniRacer::BinaryValueHandle* new_val_handle)
-> MiniRacer::BinaryValueHandle*;

/** Call JavaScript `Array.prototype.push(array, new_val)`.
*
* The result of the operation (passed into the callback) is an exception
* in case of failure.
**/
LIB_EXPORT auto mr_array_push(uint64_t context_id,
MiniRacer::BinaryValueHandle* array_handle,
MiniRacer::BinaryValueHandle* new_val_handle)
-> MiniRacer::BinaryValueHandle*;

/** Cancel the given asynchronous task.
*
* (Such tasks are started by mr_eval, mr_call_function, mr_heap_stats, and
Expand Down
45 changes: 44 additions & 1 deletion src/v8_py_frontend/object_manipulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ auto ObjectManipulator::Splice(v8::Isolate* isolate,
}

if (!splice_val->IsFunction()) {
return bv_factory_->New("splice method is not a function",
return bv_factory_->New("splice member is not a function",
type_execute_exception);
}

Expand All @@ -163,6 +163,49 @@ auto ObjectManipulator::Splice(v8::Isolate* isolate,
return bv_factory_->New(local_context, maybe_value.ToLocalChecked());
}

auto ObjectManipulator::Push(v8::Isolate* isolate,
BinaryValue* obj_ptr,
BinaryValue* new_val_ptr) -> BinaryValue::Ptr {
const v8::Isolate::Scope isolate_scope(isolate);
const v8::HandleScope handle_scope(isolate);
const v8::Local<v8::Context> local_context = context_->Get()->Get(isolate);
const v8::Context::Scope context_scope(local_context);

const v8::Local<v8::Value> local_obj_val = obj_ptr->ToValue(local_context);
const v8::Local<v8::Object> local_obj = local_obj_val.As<v8::Object>();

// Array.prototype.push doesn't exist in C++ in V8. We have to find the JS
// function and call it:
const v8::Local<v8::String> push_name =
v8::String::NewFromUtf8Literal(isolate, "push");

v8::Local<v8::Value> push_val;
if (!local_obj->Get(local_context, push_name).ToLocal(&push_val)) {
return bv_factory_->New("no push method on object", type_execute_exception);
}

if (!push_val->IsFunction()) {
return bv_factory_->New("push member is not a function",
type_execute_exception);
}

const v8::Local<v8::Function> push_func = push_val.As<v8::Function>();

const v8::TryCatch trycatch(isolate);

std::vector<v8::Local<v8::Value>> argv = {
new_val_ptr->ToValue(local_context)};

v8::MaybeLocal<v8::Value> maybe_value = push_func->Call(
local_context, local_obj, static_cast<int>(argv.size()), argv.data());
if (maybe_value.IsEmpty()) {
return bv_factory_->New(local_context, trycatch.Message(),
trycatch.Exception(), type_execute_exception);
}

return bv_factory_->New(local_context, maybe_value.ToLocalChecked());
}

auto ObjectManipulator::Call(v8::Isolate* isolate,
BinaryValue* func_ptr,
BinaryValue* this_ptr,
Expand Down
3 changes: 3 additions & 0 deletions src/v8_py_frontend/object_manipulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class ObjectManipulator {
int32_t start,
int32_t delete_count,
BinaryValue* new_val_ptr) -> BinaryValue::Ptr;
auto Push(v8::Isolate* isolate,
BinaryValue* obj_ptr,
BinaryValue* new_val_ptr) -> BinaryValue::Ptr;
auto Call(v8::Isolate* isolate,
BinaryValue* func_ptr,
BinaryValue* this_ptr,
Expand Down
Loading