Created
July 30, 2013 22:20
-
-
Save kripken/6117576 to your computer and use it in GitHub Desktop.
embind asm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/emcc b/emcc | |
index 91b109b..e40bfef 100755 | |
--- a/emcc | |
+++ b/emcc | |
@@ -1079,14 +1079,17 @@ try: | |
assert not memory_init_file, 'memory init file is not supported with module linking' | |
shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules | |
debug_level = max(debug_level, 2) | |
if shared.Settings.DLOPEN_SUPPORT: | |
shared.Settings.LINKABLE = 1 | |
+ if bind: | |
+ shared.Settings.EMBIND = 1 | |
+ | |
## Compile source code to bitcode | |
logging.debug('compiling to bitcode') | |
temp_files = [] | |
# First, generate LLVM bitcode. For each input file, we get base.o with bitcode | |
diff --git a/src/embind/embind.js b/src/embind/embind.js | |
index f0cd0c7..18bae90 100644 | |
--- a/src/embind/embind.js | |
+++ b/src/embind/embind.js | |
@@ -510,14 +510,22 @@ function new_(constructor, argumentList) { | |
dummy.prototype = constructor.prototype; | |
var obj = new dummy; | |
var r = constructor.apply(obj, argumentList); | |
return (r instanceof Object) ? r : obj; | |
} | |
+function craftFunctionTableCaller(fun) { | |
+ if (!Runtime.ASM_JS) return FUNCTION_TABLE[fun]; | |
+ return function() { | |
+ var sig = 'f' + Array.prototype.map.call(arguments, function() { return 'f' }).join(''); | |
+ return Runtime.dynCall(sig, fun, arguments); | |
+ }; | |
+} | |
+ | |
// The path to interop from JS code to C++ code: | |
// (hand-written JS code) -> (autogenerated JS invoker) -> (template-generated C++ invoker) -> (target C++ function) | |
// craftInvokerFunction generates the JS invoker function for each function exposed to JS through embind. | |
function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cppTargetFunc) { | |
// humanName: a human-readable string name for the function to be generated. | |
// argTypes: An array that contains the embind type objects for all types in the function signature. | |
// argTypes[0] is the type object for the function return value. | |
@@ -530,17 +538,17 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp | |
if (argCount < 2) { | |
throwBindingError("argTypes array size mismatch! Must at least get return value and 'this' types!"); | |
} | |
var isClassMethodFunc = (argTypes[1] !== null && classType !== null); | |
- if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) { | |
- throwBindingError('Global function '+humanName+' is not defined!'); | |
- } | |
+ //if (!isClassMethodFunc && !FUNCTION_TABLE[cppTargetFunc]) { | |
+ // throwBindingError('Global function '+humanName+' is not defined!'); | |
+ //} | |
// Free functions with signature "void function()" do not need an invoker that marshalls between wire types. | |
// TODO: This omits argument count check - enable only at -O3 or similar. | |
// if (ENABLE_UNSAFE_OPTS && argCount == 2 && argTypes[0].name == "void" && !isClassMethodFunc) { | |
// return FUNCTION_TABLE[fn]; | |
// } | |
@@ -619,15 +627,15 @@ function craftInvokerFunction(humanName, argTypes, classType, cppInvokerFunc, cp | |
var invokerFunction = new_(Function, args1).apply(null, args2); | |
return invokerFunction; | |
} | |
function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, fn) { | |
var argTypes = heap32VectorToArray(argCount, rawArgTypesAddr); | |
name = readLatin1String(name); | |
- rawInvoker = FUNCTION_TABLE[rawInvoker]; | |
+ rawInvoker = craftFunctionTableCaller(rawInvoker); | |
exposePublicSymbol(name, function() { | |
throwUnboundTypeError('Cannot call ' + name + ' due to unbound types', argTypes); | |
}, argCount - 1); | |
whenDependentTypesAreResolved([], argTypes, function(argTypes) { | |
var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */); | |
@@ -637,16 +645,16 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker, | |
} | |
var tupleRegistrations = {}; | |
function __embind_register_value_array(rawType, name, rawConstructor, rawDestructor) { | |
tupleRegistrations[rawType] = { | |
name: readLatin1String(name), | |
- rawConstructor: FUNCTION_TABLE[rawConstructor], | |
- rawDestructor: FUNCTION_TABLE[rawDestructor], | |
+ rawConstructor: craftFunctionTableCaller(rawConstructor), | |
+ rawDestructor: craftFunctionTableCaller(rawDestructor), | |
elements: [], | |
}; | |
} | |
function __embind_register_value_array_element( | |
rawTupleType, | |
getterReturnType, | |
@@ -654,18 +662,18 @@ function __embind_register_value_array_element( | |
getterContext, | |
setterArgumentType, | |
setter, | |
setterContext | |
) { | |
tupleRegistrations[rawTupleType].elements.push({ | |
getterReturnType: getterReturnType, | |
- getter: FUNCTION_TABLE[getter], | |
+ getter: craftFunctionTableCaller(getter), | |
getterContext: getterContext, | |
setterArgumentType: setterArgumentType, | |
- setter: FUNCTION_TABLE[setter], | |
+ setter: craftFunctionTableCaller(setter), | |
setterContext: setterContext, | |
}); | |
} | |
function __embind_finalize_value_array(rawTupleType) { | |
var reg = tupleRegistrations[rawTupleType]; | |
delete tupleRegistrations[rawTupleType]; | |
@@ -729,16 +737,16 @@ function __embind_register_value_object( | |
rawType, | |
name, | |
rawConstructor, | |
rawDestructor | |
) { | |
structRegistrations[rawType] = { | |
name: readLatin1String(name), | |
- rawConstructor: FUNCTION_TABLE[rawConstructor], | |
- rawDestructor: FUNCTION_TABLE[rawDestructor], | |
+ rawConstructor: craftFunctionTableCaller(rawConstructor), | |
+ rawDestructor: craftFunctionTableCaller(rawDestructor), | |
fields: [], | |
}; | |
} | |
function __embind_register_value_object_field( | |
structType, | |
fieldName, | |
@@ -748,18 +756,18 @@ function __embind_register_value_object_field( | |
setterArgumentType, | |
setter, | |
setterContext | |
) { | |
structRegistrations[structType].fields.push({ | |
fieldName: readLatin1String(fieldName), | |
getterReturnType: getterReturnType, | |
- getter: FUNCTION_TABLE[getter], | |
+ getter: craftFunctionTableCaller(getter), | |
getterContext: getterContext, | |
setterArgumentType: setterArgumentType, | |
- setter: FUNCTION_TABLE[setter], | |
+ setter: craftFunctionTableCaller(setter), | |
setterContext: setterContext, | |
}); | |
} | |
function __embind_finalize_value_object(structType) { | |
var reg = structRegistrations[structType]; | |
delete structRegistrations[structType]; | |
@@ -1232,18 +1240,18 @@ function __embind_register_class( | |
getActualType, | |
upcast, | |
downcast, | |
name, | |
rawDestructor | |
) { | |
name = readLatin1String(name); | |
- rawDestructor = FUNCTION_TABLE[rawDestructor]; | |
- getActualType = FUNCTION_TABLE[getActualType]; | |
- upcast = FUNCTION_TABLE[upcast]; | |
- downcast = FUNCTION_TABLE[downcast]; | |
+ rawDestructor = craftFunctionTableCaller(rawDestructor); | |
+ getActualType = craftFunctionTableCaller(getActualType); | |
+ upcast = craftFunctionTableCaller(upcast); | |
+ downcast = craftFunctionTableCaller(downcast); | |
var legalFunctionName = makeLegalFunctionName(name); | |
exposePublicSymbol(legalFunctionName, function() { | |
// this code cannot run if baseClassRawType is zero | |
throwUnboundTypeError('Cannot construct ' + name + ' due to unbound types', [baseClassRawType]); | |
}); | |
@@ -1329,15 +1337,15 @@ function __embind_register_class_constructor( | |
rawClassType, | |
argCount, | |
rawArgTypesAddr, | |
invoker, | |
rawConstructor | |
) { | |
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); | |
- invoker = FUNCTION_TABLE[invoker]; | |
+ invoker = craftFunctionTableCaller(invoker); | |
whenDependentTypesAreResolved([], [rawClassType], function(classType) { | |
classType = classType[0]; | |
var humanName = 'constructor ' + classType.name; | |
if (undefined === classType.registeredClass.constructor_body) { | |
classType.registeredClass.constructor_body = []; | |
@@ -1419,15 +1427,15 @@ function __embind_register_class_function( | |
argCount, | |
rawArgTypesAddr, // [ReturnType, ThisType, Args...] | |
rawInvoker, | |
context | |
) { | |
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); | |
methodName = readLatin1String(methodName); | |
- rawInvoker = FUNCTION_TABLE[rawInvoker]; | |
+ rawInvoker = craftFunctionTableCaller(rawInvoker); | |
whenDependentTypesAreResolved([], [rawClassType], function(classType) { | |
classType = classType[0]; | |
var humanName = classType.name + '.' + methodName; | |
var unboundTypesHandler = function() { | |
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); | |
@@ -1470,15 +1478,15 @@ function __embind_register_class_class_function( | |
argCount, | |
rawArgTypesAddr, | |
rawInvoker, | |
fn | |
) { | |
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr); | |
methodName = readLatin1String(methodName); | |
- rawInvoker = FUNCTION_TABLE[rawInvoker]; | |
+ rawInvoker = craftFunctionTableCaller(rawInvoker); | |
whenDependentTypesAreResolved([], [rawClassType], function(classType) { | |
classType = classType[0]; | |
var humanName = classType.name + '.' + methodName; | |
var unboundTypesHandler = function() { | |
throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes); | |
}; | |
@@ -1517,15 +1525,15 @@ function __embind_register_class_property( | |
getter, | |
getterContext, | |
setterArgumentType, | |
setter, | |
setterContext | |
) { | |
fieldName = readLatin1String(fieldName); | |
- getter = FUNCTION_TABLE[getter]; | |
+ getter = craftFunctionTableCaller(getter); | |
whenDependentTypesAreResolved([], [classType], function(classType) { | |
classType = classType[0]; | |
var humanName = classType.name + '.' + fieldName; | |
var desc = { | |
get: function() { | |
throwUnboundTypeError('Cannot access ' + humanName + ' due to unbound types', [getterReturnType, setterArgumentType]); | |
@@ -1555,15 +1563,15 @@ function __embind_register_class_property( | |
var ptr = validateThis(this, classType, humanName + ' getter'); | |
return getterReturnType['fromWireType'](getter(getterContext, ptr)); | |
}, | |
enumerable: true | |
}; | |
if (setter) { | |
- setter = FUNCTION_TABLE[setter]; | |
+ setter = craftFunctionTableCaller(setter); | |
var setterArgumentType = types[1]; | |
desc.set = function(v) { | |
var ptr = validateThis(this, classType, humanName + ' setter'); | |
var destructors = []; | |
setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, v)); | |
runDestructors(destructors); | |
}; | |
@@ -1596,18 +1604,18 @@ function __embind_register_smart_ptr( | |
sharingPolicy, | |
rawGetPointee, | |
rawConstructor, | |
rawShare, | |
rawDestructor | |
) { | |
name = readLatin1String(name); | |
- rawGetPointee = FUNCTION_TABLE[rawGetPointee]; | |
- rawConstructor = FUNCTION_TABLE[rawConstructor]; | |
- rawShare = FUNCTION_TABLE[rawShare]; | |
- rawDestructor = FUNCTION_TABLE[rawDestructor]; | |
+ rawGetPointee = craftFunctionTableCaller(rawGetPointee); | |
+ rawConstructor = craftFunctionTableCaller(rawConstructor); | |
+ rawShare = craftFunctionTableCaller(rawShare); | |
+ rawDestructor = craftFunctionTableCaller(rawDestructor); | |
whenDependentTypesAreResolved([rawType], [rawPointeeType], function(pointeeType) { | |
pointeeType = pointeeType[0]; | |
var registeredPointer = new RegisteredPointer( | |
name, | |
pointeeType.registeredClass, | |
diff --git a/src/modules.js b/src/modules.js | |
index 9f41923..e6f5007 100644 | |
--- a/src/modules.js | |
+++ b/src/modules.js | |
@@ -319,14 +319,24 @@ var Functions = { | |
if (!tables[sig]) tables[sig] = zeros(firstTableIndex); | |
var index = this.indexedFunctions[ident]; | |
for (var i = tables[sig].length; i < index; i++) { | |
tables[sig][i] = 0; // keep flat | |
} | |
tables[sig][index] = ident; | |
} | |
+ if (ASM_JS && EMBIND) { | |
+ // ensure f..f functions for each size, so embind can always call f..f sigs | |
+ keys(tables).forEach(function(t) { | |
+ var fsig = t.replace(/i/g, 'f').replace('v', 'f'); | |
+ if (!(fsig in tables)) { | |
+printErr('add ' + [t, fsig]); | |
+ tables[fsig] = zeros(firstTableIndex); | |
+ } | |
+ }); | |
+ } | |
var generated = false; | |
var wrapped = {}; | |
var maxTable = 0; | |
for (var t in tables) { | |
if (t == 'pre') continue; | |
generated = true; | |
var table = tables[t]; | |
diff --git a/src/runtime.js b/src/runtime.js | |
index e07d505..4a8a130 100644 | |
--- a/src/runtime.js | |
+++ b/src/runtime.js | |
@@ -179,14 +179,16 @@ var Runtime = { | |
dedup: dedup, | |
set: set, | |
STACK_ALIGN: {{{ STACK_ALIGN }}}, | |
+ ASM_JS: {{{ ASM_JS }}}, | |
+ | |
// type can be a native type or a struct (or null, for structs we only look at size here) | |
getAlignSize: function(type, size, vararg) { | |
// we align i64s and doubles on 64-bit boundaries, unlike x86 | |
#if TARGET_LE32 | |
if (type == 'i64' || type == 'double' || vararg) return 8; | |
if (!type) return Math.min(size, 8); // align structures internally to 64 bits | |
#endif | |
@@ -372,20 +374,21 @@ var Runtime = { | |
} | |
}, | |
funcWrappers: {}, | |
getFuncWrapper: function(func, sig) { | |
assert(sig); | |
- if (!Runtime.funcWrappers[func]) { | |
- Runtime.funcWrappers[func] = function() { | |
+ var id = func + '|' + sig; | |
+ if (!Runtime.funcWrappers[id]) { | |
+ Runtime.funcWrappers[id] = function() { | |
return Runtime.dynCall(sig, func, arguments); | |
}; | |
} | |
- return Runtime.funcWrappers[func]; | |
+ return Runtime.funcWrappers[id]; | |
}, | |
// Returns a processor of UTF. | |
// processCChar() receives characters from a C-like UTF representation and returns JS string fragments. | |
// processJSString() receives a JS string and returns a C-like UTF representation in an array | |
UTF8Processor: function() { | |
var buffer = []; | |
diff --git a/src/settings.js b/src/settings.js | |
index ed2afdb..297a2ff 100644 | |
--- a/src/settings.js | |
+++ b/src/settings.js | |
@@ -259,14 +259,15 @@ var EXPORTED_FUNCTIONS = ['_main', '_malloc']; | |
// through LLVM dead code elimination, and also made accessible outside of | |
// the generated code even after running closure compiler (on "Module"). | |
// Note the necessary prefix of "_". | |
var EXPORT_ALL = 0; // If true, we export all the symbols. Note that this does *not* affect LLVM, so it can | |
// still eliminate functions as dead. This just exports them on the Module object. | |
var EXPORT_BINDINGS = 0; // Export all bindings generator functions (prefixed with emscripten_bind_). This | |
// is necessary to use the bindings generator with asm.js | |
+var EMBIND = 0; // Whether the embind bindings approach is used | |
// JS library functions (C functions implemented in JS) | |
// that we include by default. If you want to make sure | |
// something is included by the JS compiler, add it here. | |
// For example, if you do not use some emscripten_* | |
// C API call from C, but you want to call it from JS, | |
// add it here (and in EXPORTED FUNCTIONS with prefix | |
diff --git a/tests/runner.py b/tests/runner.py | |
index 47e18ed..976772c 100755 | |
--- a/tests/runner.py | |
+++ b/tests/runner.py | |
@@ -9328,14 +9328,34 @@ def process(filename): | |
printf("abs(-11): %d\n", Math["abs"](-11).as<int>()); | |
return 0; | |
} | |
''' | |
self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); | |
+ def test_embind_2(self): | |
+ if self.emcc_args is None: return self.skip('requires emcc') | |
+ Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] | |
+ open('post.js', 'w').write(''' | |
+ Module.print('lerp ' + Module.lerp(1, 2, 0.66) + '.'); | |
+ ''') | |
+ src = r''' | |
+ #include <stdio.h> | |
+ #include <SDL/SDL.h> | |
+ #include <emscripten/bind.h> | |
+ using namespace emscripten; | |
+ float lerp(float a, float b, float t) { | |
+ return (1 - t) * a + t * b; | |
+ } | |
+ EMSCRIPTEN_BINDINGS(my_module) { | |
+ function("lerp", &lerp); | |
+ } | |
+ ''' | |
+ self.do_run(src, 'lerp 1.66'); | |
+ | |
def test_scriptaclass(self): | |
if self.emcc_args is None: return self.skip('requires emcc') | |
Settings.EXPORT_BINDINGS = 1 | |
header_filename = os.path.join(self.get_dir(), 'header.h') | |
header = ''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment