Skip to content

Commit b8b5201

Browse files
committed
Reduce calls to get array length
1 parent 47d3e26 commit b8b5201

File tree

10 files changed

+113
-2
lines changed

10 files changed

+113
-2
lines changed

src/py_mini_racer/_abstract_context.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ def array_insert(
7878
) -> None:
7979
pass
8080

81+
@abstractmethod
82+
def array_push(self, arr: JSArray, new_val: PythonJSConvertedTypes) -> None:
83+
pass
84+
8185
@abstractmethod
8286
def call_function(
8387
self,

src/py_mini_racer/_context.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@ def array_insert(
199199
)
200200
).to_python_or_raise()
201201

202+
def array_push(self, arr: JSArray, new_val: PythonJSConvertedTypes) -> None:
203+
arr_handle = python_to_value_handle(self, arr)
204+
new_val_handle = python_to_value_handle(self, new_val)
205+
206+
# Convert the value just to convert any exceptions (and GC the result)
207+
self._wrap_raw_handle(
208+
self._get_dll().mr_array_push(self._ctx, arr_handle.raw, new_val_handle.raw)
209+
).to_python_or_raise()
210+
202211
def call_function(
203212
self,
204213
func: JSFunction,

src/py_mini_racer/_dll.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ def _build_dll_handle(dll_path: Path) -> ctypes.CDLL: # noqa: PLR0915
134134
]
135135
handle.mr_splice_array.restype = RawValueHandle
136136

137+
handle.mr_array_push.argtypes = [ctypes.c_uint64, RawValueHandle, RawValueHandle]
138+
handle.mr_array_push.restype = RawValueHandle
139+
137140
handle.mr_call_function.argtypes = [
138141
ctypes.c_uint64,
139142
RawValueHandle,

src/py_mini_racer/_objects.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ def insert(self, index: int, new_obj: PythonJSConvertedTypes) -> None:
132132

133133
def __iter__(self) -> Iterator[PythonJSConvertedTypes]:
134134
for i in range(len(self)):
135-
yield self[i]
135+
yield self._ctx.get_object_item(self, i)
136+
137+
def append(self, value: PythonJSConvertedTypes) -> None:
138+
self._ctx.array_push(self, value)
136139

137140

138141
class JSFunctionImpl(JSMappedObjectImpl, JSFunction):

src/v8_py_frontend/context.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,29 @@ auto Context::SpliceArray(BinaryValueHandle* obj_handle,
277277
.get());
278278
}
279279

280+
auto Context::ArrayPush(BinaryValueHandle* obj_handle,
281+
BinaryValueHandle* new_val_handle)
282+
-> BinaryValueHandle* {
283+
auto obj_hc = MakeHandleConverter(obj_handle, "Bad handle: obj");
284+
if (!obj_hc) {
285+
return obj_hc.GetErrorHandle();
286+
}
287+
288+
auto new_val_hc = MakeHandleConverter(new_val_handle, "Bad handle: new_val");
289+
if (!new_val_hc) {
290+
return new_val_hc.GetErrorHandle();
291+
}
292+
293+
return bv_registry_.Remember(
294+
isolate_manager_
295+
.Run([this, obj_ptr = obj_hc.GetPtr(),
296+
new_val_ptr = new_val_hc.GetPtr()](v8::Isolate* isolate) {
297+
return object_manipulator_.Push(isolate, obj_ptr.get(),
298+
new_val_ptr.get());
299+
})
300+
.get());
301+
}
302+
280303
void Context::FreeBinaryValue(BinaryValueHandle* val) {
281304
bv_registry_.Forget(val);
282305
}

src/v8_py_frontend/context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class Context {
6060
int32_t start,
6161
int32_t delete_count,
6262
BinaryValueHandle* new_val_handle) -> BinaryValueHandle*;
63+
auto ArrayPush(BinaryValueHandle* obj_handle,
64+
BinaryValueHandle* new_val_handle) -> BinaryValueHandle*;
6365
auto CallFunction(BinaryValueHandle* func_handle,
6466
BinaryValueHandle* this_handle,
6567
BinaryValueHandle* argv_handle,

src/v8_py_frontend/exports.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,17 @@ LIB_EXPORT auto mr_splice_array(uint64_t context_id,
249249
new_val_handle);
250250
}
251251

252+
LIB_EXPORT auto mr_array_push(uint64_t context_id,
253+
MiniRacer::BinaryValueHandle* array_handle,
254+
MiniRacer::BinaryValueHandle* new_val_handle)
255+
-> MiniRacer::BinaryValueHandle* {
256+
auto context = GetContext(context_id);
257+
if (!context) {
258+
return nullptr;
259+
}
260+
return context->ArrayPush(array_handle, new_val_handle);
261+
}
262+
252263
LIB_EXPORT auto mr_call_function(uint64_t context_id,
253264
MiniRacer::BinaryValueHandle* func_handle,
254265
MiniRacer::BinaryValueHandle* this_handle,

src/v8_py_frontend/exports.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ LIB_EXPORT auto mr_splice_array(uint64_t context_id,
197197
MiniRacer::BinaryValueHandle* new_val_handle)
198198
-> MiniRacer::BinaryValueHandle*;
199199

200+
/** Call JavaScript `Array.prototype.push(array, new_val)`.
201+
*
202+
* The result of the operation (passed into the callback) is an exception
203+
* in case of failure.
204+
**/
205+
LIB_EXPORT auto mr_array_push(uint64_t context_id,
206+
MiniRacer::BinaryValueHandle* array_handle,
207+
MiniRacer::BinaryValueHandle* new_val_handle)
208+
-> MiniRacer::BinaryValueHandle*;
209+
200210
/** Cancel the given asynchronous task.
201211
*
202212
* (Such tasks are started by mr_eval, mr_call_function, mr_heap_stats, and

src/v8_py_frontend/object_manipulator.cc

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ auto ObjectManipulator::Splice(v8::Isolate* isolate,
137137
}
138138

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

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

166+
auto ObjectManipulator::Push(v8::Isolate* isolate,
167+
BinaryValue* obj_ptr,
168+
BinaryValue* new_val_ptr) -> BinaryValue::Ptr {
169+
const v8::Isolate::Scope isolate_scope(isolate);
170+
const v8::HandleScope handle_scope(isolate);
171+
const v8::Local<v8::Context> local_context = context_->Get()->Get(isolate);
172+
const v8::Context::Scope context_scope(local_context);
173+
174+
const v8::Local<v8::Value> local_obj_val = obj_ptr->ToValue(local_context);
175+
const v8::Local<v8::Object> local_obj = local_obj_val.As<v8::Object>();
176+
177+
// Array.prototype.push doesn't exist in C++ in V8. We have to find the JS
178+
// function and call it:
179+
const v8::Local<v8::String> push_name =
180+
v8::String::NewFromUtf8Literal(isolate, "push");
181+
182+
v8::Local<v8::Value> push_val;
183+
if (!local_obj->Get(local_context, push_name).ToLocal(&push_val)) {
184+
return bv_factory_->New("no push method on object", type_execute_exception);
185+
}
186+
187+
if (!push_val->IsFunction()) {
188+
return bv_factory_->New("push member is not a function",
189+
type_execute_exception);
190+
}
191+
192+
const v8::Local<v8::Function> push_func = push_val.As<v8::Function>();
193+
194+
const v8::TryCatch trycatch(isolate);
195+
196+
std::vector<v8::Local<v8::Value>> argv = {
197+
new_val_ptr->ToValue(local_context)};
198+
199+
v8::MaybeLocal<v8::Value> maybe_value = push_func->Call(
200+
local_context, local_obj, static_cast<int>(argv.size()), argv.data());
201+
if (maybe_value.IsEmpty()) {
202+
return bv_factory_->New(local_context, trycatch.Message(),
203+
trycatch.Exception(), type_execute_exception);
204+
}
205+
206+
return bv_factory_->New(local_context, maybe_value.ToLocalChecked());
207+
}
208+
166209
auto ObjectManipulator::Call(v8::Isolate* isolate,
167210
BinaryValue* func_ptr,
168211
BinaryValue* this_ptr,

src/v8_py_frontend/object_manipulator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class ObjectManipulator {
3838
int32_t start,
3939
int32_t delete_count,
4040
BinaryValue* new_val_ptr) -> BinaryValue::Ptr;
41+
auto Push(v8::Isolate* isolate,
42+
BinaryValue* obj_ptr,
43+
BinaryValue* new_val_ptr) -> BinaryValue::Ptr;
4144
auto Call(v8::Isolate* isolate,
4245
BinaryValue* func_ptr,
4346
BinaryValue* this_ptr,

0 commit comments

Comments
 (0)