Skip to content

Commit 12777ca

Browse files
authored
[emval] Reuse method calling optimisation in regular calls (#20383)
1 parent 94f726a commit 12777ca

File tree

3 files changed

+115
-179
lines changed

3 files changed

+115
-179
lines changed

src/embind/emval.js

Lines changed: 68 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
/*jslint sub:true*/ /* The symbols 'fromWireType' and 'toWireType' must be accessed via array notation to be closure-safe since craftInvokerFunction crafts functions as strings that can't be closured. */
1313

1414
// -- jshint doesn't understand library syntax, so we need to mark the symbols exposed here
15-
/*global getStringOrSymbol, emval_handles, Emval, __emval_unregister, count_emval_handles, emval_symbols, __emval_decref, emval_newers*/
16-
/*global craftEmvalAllocator, emval_addMethodCaller, emval_methodCallers, addToLibrary, global, emval_lookupTypes, makeLegalFunctionName*/
15+
/*global getStringOrSymbol, emval_handles, Emval, __emval_unregister, count_emval_handles, emval_symbols, __emval_decref*/
16+
/*global emval_addMethodCaller, emval_methodCallers, addToLibrary, global, emval_lookupTypes, makeLegalFunctionName*/
1717
/*global emval_get_global*/
1818

1919
var LibraryEmVal = {
@@ -145,79 +145,6 @@ var LibraryEmVal = {
145145
return Emval.toHandle(v);
146146
},
147147

148-
$emval_newers: {}, // arity -> function
149-
$craftEmvalAllocator__deps: ['$Emval', '$requireRegisteredType'],
150-
$craftEmvalAllocator: (argCount) => {
151-
/*This function returns a new function that looks like this:
152-
function emval_allocator_3(constructor, argTypes, args) {
153-
var argType0 = requireRegisteredType(HEAP32[(argTypes >> 2)], "parameter 0");
154-
var arg0 = argType0['readValueFromPointer'](args);
155-
var argType1 = requireRegisteredType(HEAP32[(argTypes >> 2) + 1], "parameter 1");
156-
var arg1 = argType1['readValueFromPointer'](args + 8);
157-
var argType2 = requireRegisteredType(HEAP32[(argTypes >> 2) + 2], "parameter 2");
158-
var arg2 = argType2['readValueFromPointer'](args + 16);
159-
var obj = new constructor(arg0, arg1, arg2);
160-
return Emval.toHandle(obj);
161-
} */
162-
#if !DYNAMIC_EXECUTION
163-
var argsList = new Array(argCount + 1);
164-
return function(constructor, argTypes, args) {
165-
argsList[0] = constructor;
166-
for (var i = 0; i < argCount; ++i) {
167-
var argType = requireRegisteredType({{{ makeGetValue('argTypes', 'i * ' + POINTER_SIZE, '*') }}}, 'parameter ' + i);
168-
argsList[i + 1] = argType['readValueFromPointer'](args);
169-
args += argType['argPackAdvance'];
170-
}
171-
var obj = new (constructor.bind.apply(constructor, argsList));
172-
return Emval.toHandle(obj);
173-
};
174-
#else
175-
var argsList = "";
176-
for (var i = 0; i < argCount; ++i) {
177-
argsList += (i!==0?", ":"")+"arg"+i; // 'arg0, arg1, ..., argn'
178-
}
179-
180-
// The body of the generated function does not have access to enclosing
181-
// scope where HEAPU64/HEAPU32/etc are defined, and we cannot pass them
182-
// directly as arguments (like we do the Module object) since memory
183-
// growth can cause them to be re-bound.
184-
var getMemory = () => {{{ MEMORY64 ? "HEAPU64" : "HEAPU32" }}};
185-
186-
var functionBody =
187-
"return function emval_allocator_"+argCount+"(constructor, argTypes, args) {\n" +
188-
" var {{{ MEMORY64 ? 'HEAPU64' : 'HEAPU32' }}} = getMemory();\n";
189-
190-
for (var i = 0; i < argCount; ++i) {
191-
functionBody +=
192-
"var argType"+i+" = requireRegisteredType({{{ makeGetValue('argTypes', '0', '*') }}}, 'parameter "+i+"');\n" +
193-
"var arg"+i+" = argType"+i+".readValueFromPointer(args);\n" +
194-
"args += argType"+i+"['argPackAdvance'];\n" +
195-
"argTypes += {{{ POINTER_SIZE }}};\n";
196-
}
197-
functionBody +=
198-
"var obj = new constructor("+argsList+");\n" +
199-
"return valueToHandle(obj);\n" +
200-
"}\n";
201-
202-
/*jshint evil:true*/
203-
return (new Function("requireRegisteredType", "Module", "valueToHandle", "getMemory" , functionBody))(
204-
requireRegisteredType, Module, Emval.toHandle, getMemory);
205-
#endif
206-
},
207-
208-
_emval_new__deps: ['$craftEmvalAllocator', '$emval_newers', '$Emval'],
209-
_emval_new: (handle, argCount, argTypes, args) => {
210-
handle = Emval.toValue(handle);
211-
212-
var newer = emval_newers[argCount];
213-
if (!newer) {
214-
newer = craftEmvalAllocator(argCount);
215-
emval_newers[argCount] = newer;
216-
}
217-
218-
return newer(handle, argTypes, args);
219-
},
220-
221148
#if !DYNAMIC_EXECUTION
222149
$emval_get_global: () => {
223150
if (typeof globalThis == 'object') {
@@ -286,14 +213,22 @@ var LibraryEmVal = {
286213
handle[key] = value;
287214
},
288215

289-
_emval_as__deps: ['$Emval', '$requireRegisteredType'],
216+
$emval_returnValue__deps: ['$Emval'],
217+
$emval_returnValue: (returnType, destructorsRef, handle) => {
218+
var destructors = [];
219+
var result = returnType['toWireType'](destructors, handle);
220+
if (destructors.length) {
221+
// void, primitives and any other types w/o destructors don't need to allocate a handle
222+
{{{ makeSetValue('destructorsRef', '0', 'Emval.toHandle(destructors)', '*') }}};
223+
}
224+
return result;
225+
},
226+
227+
_emval_as__deps: ['$Emval', '$requireRegisteredType', '$emval_returnValue'],
290228
_emval_as: (handle, returnType, destructorsRef) => {
291229
handle = Emval.toValue(handle);
292230
returnType = requireRegisteredType(returnType, 'emval::as');
293-
var destructors = [];
294-
var rd = Emval.toHandle(destructors);
295-
{{{ makeSetValue('destructorsRef', '0', 'rd', '*') }}};
296-
return returnType['toWireType'](destructors, handle);
231+
return emval_returnValue(returnType, destructorsRef, handle);
297232
},
298233

299234
_emval_as_int64__deps: ['$Emval', '$requireRegisteredType'],
@@ -344,20 +279,11 @@ var LibraryEmVal = {
344279
return !object;
345280
},
346281

347-
_emval_call__deps: ['$emval_lookupTypes', '$Emval'],
348-
_emval_call: (handle, argCount, argTypes, argv) => {
282+
_emval_call__deps: ['$emval_methodCallers', '$Emval'],
283+
_emval_call: (caller, handle, destructorsRef, args) => {
284+
caller = emval_methodCallers[caller];
349285
handle = Emval.toValue(handle);
350-
var types = emval_lookupTypes(argCount, argTypes);
351-
352-
var args = new Array(argCount);
353-
for (var i = 0; i < argCount; ++i) {
354-
var type = types[i];
355-
args[i] = type['readValueFromPointer'](argv);
356-
argv += type['argPackAdvance'];
357-
}
358-
359-
var rv = handle.apply(undefined, args);
360-
return Emval.toHandle(rv);
286+
return caller(null, handle, destructorsRef, args);
361287
},
362288

363289
$emval_lookupTypes__deps: ['$requireRegisteredType'],
@@ -381,70 +307,89 @@ var LibraryEmVal = {
381307
return id;
382308
},
383309

310+
#if MIN_CHROME_VERSION < 49 || MIN_EDGE_VERSION < 12 || MIN_FIREFOX_VERSION < 42 || MIN_IE_VERSION != TARGET_NOT_SUPPORTED || MIN_SAFARI_VERSION < 100101
311+
$reflectConstruct: null,
312+
$reflectConstruct__postset: `
313+
if (typeof Reflect !== 'undefined') {
314+
reflectConstruct = Reflect.construct;
315+
} else {
316+
reflectConstruct = function(target, args) {
317+
// limited polyfill for Reflect.construct that handles variadic args and native objects, but not new.target
318+
return new (target.bind.apply(target, [null].concat(args)))();
319+
};
320+
}
321+
`,
322+
#else
323+
$reflectConstruct: 'Reflect.construct',
324+
#endif
325+
384326
_emval_get_method_caller__deps: [
385-
'$emval_addMethodCaller', '$emval_lookupTypes',,
327+
'$emval_addMethodCaller', '$emval_lookupTypes',
386328
'$makeLegalFunctionName',
329+
'$reflectConstruct', '$emval_returnValue',
387330
#if DYNAMIC_EXECUTION
388331
'$newFunc',
389332
#endif
390333
],
391-
_emval_get_method_caller: (argCount, argTypes) => {
334+
_emval_get_method_caller: (argCount, argTypes, kind) => {
392335
var types = emval_lookupTypes(argCount, argTypes);
393336
var retType = types.shift();
394337
argCount--; // remove the shifted off return type
395338

396339
#if !DYNAMIC_EXECUTION
397340
var argN = new Array(argCount);
398-
var invokerFunction = (handle, name, destructors, args) => {
341+
var invokerFunction = (obj, func, destructorsRef, args) => {
399342
var offset = 0;
400343
for (var i = 0; i < argCount; ++i) {
401344
argN[i] = types[i]['readValueFromPointer'](args + offset);
402345
offset += types[i]['argPackAdvance'];
403346
}
404-
var rv = handle[name].apply(handle, argN);
347+
var rv = kind === /* CONSTRUCTOR */ 1 ? reflectConstruct(func, argN) : func.apply(obj, argN);
405348
for (var i = 0; i < argCount; ++i) {
406349
if (types[i].deleteObject) {
407350
types[i].deleteObject(argN[i]);
408351
}
409352
}
410-
return retType['toWireType'](destructors, rv);
353+
return emval_returnValue(retType, destructorsRef, rv);
411354
};
412355
#else
413-
var params = ["retType"];
414-
var args = [retType];
415-
416-
var argsList = ""; // 'arg0, arg1, arg2, ... , argN'
417-
for (var i = 0; i < argCount; ++i) {
418-
argsList += (i !== 0 ? ", " : "") + "arg" + i;
419-
params.push("argType" + i);
420-
args.push(types[i]);
421-
}
422-
423356
var signatureName = retType.name + "_$" + types.map(t => t.name).join("_") + "$";
424357
var functionName = makeLegalFunctionName("methodCaller_" + signatureName);
425358
var functionBody =
426-
"return function " + functionName + "(handle, name, destructors, args) {\n";
359+
`return function ${functionName}(obj, func, destructorsRef, args) {\n`;
427360

428361
var offset = 0;
362+
var argsList = []; // 'obj?, arg0, arg1, arg2, ... , argN'
363+
if (kind === /* FUNCTION */ 0) {
364+
argsList.push("obj");
365+
}
366+
var params = ["retType"];
367+
var args = [retType];
429368
for (var i = 0; i < argCount; ++i) {
430-
functionBody +=
431-
" var arg" + i + " = argType" + i + ".readValueFromPointer(args" + (offset ? ("+"+offset) : "") + ");\n";
432-
offset += types[i]['argPackAdvance'];
369+
argsList.push("arg" + i);
370+
params.push("argType" + i);
371+
args.push(types[i]);
372+
functionBody +=
373+
` var arg${i} = argType${i}.readValueFromPointer(args${offset ? "+" + offset : ""});\n`;
374+
offset += types[i]['argPackAdvance'];
433375
}
376+
var invoker = kind === /* CONSTRUCTOR */ 1 ? 'new func' : 'func.call';
434377
functionBody +=
435-
" var rv = handle[name](" + argsList + ");\n";
378+
` var rv = ${invoker}(${argsList.join(", ")});\n`;
436379
for (var i = 0; i < argCount; ++i) {
437-
if (types[i]['deleteObject']) {
438-
functionBody +=
439-
" argType" + i + ".deleteObject(arg" + i + ");\n";
440-
}
380+
if (types[i]['deleteObject']) {
381+
functionBody +=
382+
` argType${i}.deleteObject(arg${i});\n`;
383+
}
441384
}
442385
if (!retType.isVoid) {
443-
functionBody +=
444-
" return retType.toWireType(destructors, rv);\n";
386+
params.push("emval_returnValue");
387+
args.push(emval_returnValue);
388+
functionBody +=
389+
" return emval_returnValue(retType, destructorsRef, rv);\n";
445390
}
446391
functionBody +=
447-
"};\n";
392+
"};\n";
448393

449394
params.push(functionBody);
450395
var invokerFunction = newFunc(Function, params).apply(null, args);
@@ -453,17 +398,11 @@ var LibraryEmVal = {
453398
},
454399

455400
_emval_call_method__deps: ['$getStringOrSymbol', '$emval_methodCallers', '$Emval'],
456-
_emval_call_method: (caller, handle, methodName, destructorsRef, args) => {
401+
_emval_call_method: (caller, objHandle, methodName, destructorsRef, args) => {
457402
caller = emval_methodCallers[caller];
458-
handle = Emval.toValue(handle);
403+
objHandle = Emval.toValue(objHandle);
459404
methodName = getStringOrSymbol(methodName);
460-
var destructors = [];
461-
var result = caller(handle, methodName, destructors, args);
462-
// void and any other types w/o destructors don't need to allocate a handle
463-
if (destructors.length) {
464-
{{{ makeSetValue('destructorsRef', '0', 'Emval.toHandle(destructors)', '*') }}};
465-
}
466-
return result;
405+
return caller(objHandle, objHandle[methodName], destructorsRef, args);
467406
},
468407

469408
_emval_typeof__deps: ['$Emval'],

src/library_sigs.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,13 @@ sigs = {
337337
_emval_as_int64__sig: 'jpp',
338338
_emval_as_uint64__sig: 'jpp',
339339
_emval_await__sig: 'pp',
340-
_emval_call__sig: 'ppipp',
340+
_emval_call__sig: 'dpppp',
341341
_emval_call_method__sig: 'dppppp',
342342
_emval_decref__sig: 'vp',
343343
_emval_delete__sig: 'ipp',
344344
_emval_equals__sig: 'ipp',
345345
_emval_get_global__sig: 'pp',
346-
_emval_get_method_caller__sig: 'pip',
346+
_emval_get_method_caller__sig: 'pipi',
347347
_emval_get_module_property__sig: 'pp',
348348
_emval_get_property__sig: 'ppp',
349349
_emval_greater_than__sig: 'ipp',
@@ -355,7 +355,6 @@ sigs = {
355355
_emval_iter_begin__sig: 'pp',
356356
_emval_iter_next__sig: 'pp',
357357
_emval_less_than__sig: 'ipp',
358-
_emval_new__sig: 'ppipp',
359358
_emval_new_array__sig: 'p',
360359
_emval_new_array_from_memory_view__sig: 'pp',
361360
_emval_new_cstring__sig: 'pp',

0 commit comments

Comments
 (0)