aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js16
-rwxr-xr-xsrc/embind/embind.js631
-rwxr-xr-xsrc/embind/emval.js60
-rw-r--r--src/jsifier.js60
-rw-r--r--src/library.js63
-rw-r--r--src/library_browser.js6
-rw-r--r--src/library_gl.js8
-rw-r--r--src/preamble.js2
-rw-r--r--src/settings.js1
9 files changed, 615 insertions, 232 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index df5a435e..3278139b 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -1389,21 +1389,21 @@ function analyzer(data, sidePass) {
var line = label.lines[j];
if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
// Add a new label
- var oldIdent = label.ident;
- var newIdent = func.labelIdCounter++;
+ var oldLabel = label.ident;
+ var newLabel = func.labelIdCounter++;
if (!func.setjmpTable) func.setjmpTable = [];
- func.setjmpTable.push([oldIdent, newIdent, line.assignTo]);
+ func.setjmpTable.push({ oldLabel: oldLabel, newLabel: newLabel, assignTo: line.assignTo });
func.labels.splice(i+1, 0, {
intertype: 'label',
- ident: newIdent,
+ ident: newLabel,
lineNum: label.lineNum + 0.5,
lines: label.lines.slice(j+1)
});
- func.labelsDict[newIdent] = func.labels[i+1];
+ func.labelsDict[newLabel] = func.labels[i+1];
label.lines = label.lines.slice(0, j+1);
label.lines.push({
intertype: 'branch',
- label: toNiceIdent(newIdent),
+ label: toNiceIdent(newLabel),
lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
});
// Correct phis
@@ -1412,8 +1412,8 @@ function analyzer(data, sidePass) {
if (phi.intertype == 'phi') {
for (var i = 0; i < phi.params.length; i++) {
var sourceLabelId = getActualLabelId(phi.params[i].label);
- if (sourceLabelId == oldIdent) {
- phi.params[i].label = newIdent;
+ if (sourceLabelId == oldLabel) {
+ phi.params[i].label = newLabel;
}
}
}
diff --git a/src/embind/embind.js b/src/embind/embind.js
index ee717f4b..988526b4 100755
--- a/src/embind/embind.js
+++ b/src/embind/embind.js
@@ -1,10 +1,10 @@
/*global Module*/
/*global _malloc, _free, _memcpy*/
-/*global FUNCTION_TABLE, HEAP32, HEAPU8*/
-/*global Pointer_stringify*/
+/*global FUNCTION_TABLE, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32*/
+/*global readLatin1String*/
/*global __emval_register, _emval_handle_array, __emval_decref*/
/*global ___getTypeName*/
-
+/*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. */
var InternalError = Module.InternalError = extendError(Error, 'InternalError');
var BindingError = Module.BindingError = extendError(Error, 'BindingError');
var UnboundTypeError = Module.UnboundTypeError = extendError(BindingError, 'UnboundTypeError');
@@ -223,9 +223,26 @@ function whenDependentTypesAreResolved(myTypes, dependentTypes, getTypeConverter
}
}
+var __charCodes = (function() {
+ var codes = new Array(256);
+ for (var i = 0; i < 256; ++i) {
+ codes[i] = String.fromCharCode(i);
+ }
+ return codes;
+})();
+
+function readLatin1String(ptr) {
+ var ret = "";
+ var c = ptr;
+ while (HEAPU8[c]) {
+ ret += __charCodes[HEAPU8[c++]];
+ }
+ return ret;
+}
+
function getTypeName(type) {
var ptr = ___getTypeName(type);
- var rv = Pointer_stringify(ptr);
+ var rv = readLatin1String(ptr);
_free(ptr);
return rv;
}
@@ -247,34 +264,35 @@ function requireRegisteredType(rawType, humanName) {
}
function __embind_register_void(rawType, name) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function() {
+ 'fromWireType': function() {
return undefined;
},
});
}
function __embind_register_bool(rawType, name, trueValue, falseValue) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(wt) {
+ 'fromWireType': function(wt) {
// ambiguous emscripten ABI: sometimes return values are
// true or false, and sometimes integers (0 or 1)
return !!wt;
},
- toWireType: function(destructors, o) {
+ 'toWireType': function(destructors, o) {
return o ? trueValue : falseValue;
},
+ destructorFunction: null, // This type does not need a destructor
});
}
// When converting a number from JS to C++ side, the valid range of the number is
// [minRange, maxRange], inclusive.
function __embind_register_integer(primitiveType, name, minRange, maxRange) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
if (maxRange === -1) { // LLVM doesn't have signed and unsigned 32-bit types, so u32 literals come out as 'i32 -1'. Always treat those as max u32.
maxRange = 4294967295;
}
@@ -282,10 +300,10 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) {
name: name,
minRange: minRange,
maxRange: maxRange,
- fromWireType: function(value) {
+ 'fromWireType': function(value) {
return value;
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following two if()s and assume value is of proper type.
if (typeof value !== "number") {
@@ -296,17 +314,18 @@ function __embind_register_integer(primitiveType, name, minRange, maxRange) {
}
return value | 0;
},
+ destructorFunction: null, // This type does not need a destructor
});
}
function __embind_register_float(rawType, name) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(value) {
+ 'fromWireType': function(value) {
return value;
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations: we could
// avoid the following if() and assume value is of proper type.
if (typeof value !== "number") {
@@ -314,15 +333,16 @@ function __embind_register_float(rawType, name) {
}
return value;
},
+ destructorFunction: null, // This type does not need a destructor
});
}
-function __embind_register_cstring(rawType, name) {
- name = Pointer_stringify(name);
+function __embind_register_std_string(rawType, name) {
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(value) {
- var length = HEAP32[value >> 2];
+ 'fromWireType': function(value) {
+ var length = HEAPU32[value >> 2];
var a = new Array(length);
for (var i = 0; i < length; ++i) {
a[i] = String.fromCharCode(HEAPU8[value + 4 + i]);
@@ -330,32 +350,102 @@ function __embind_register_cstring(rawType, name) {
_free(value);
return a.join('');
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
+ if (value instanceof ArrayBuffer) {
+ value = new Uint8Array(value);
+ }
+
+ function getTAElement(ta, index) {
+ return ta[index];
+ }
+ function getStringElement(string, index) {
+ return string.charCodeAt(index);
+ }
+ var getElement;
+ if (value instanceof Uint8Array) {
+ getElement = getTAElement;
+ } else if (value instanceof Int8Array) {
+ getElement = getTAElement;
+ } else if (typeof value === 'string') {
+ getElement = getStringElement;
+ } else {
+ throwBindingError('Cannot pass non-string to std::string');
+ }
+
// assumes 4-byte alignment
var length = value.length;
var ptr = _malloc(4 + length);
- HEAP32[ptr >> 2] = length;
+ HEAPU32[ptr >> 2] = length;
+ for (var i = 0; i < length; ++i) {
+ var charCode = getElement(value, i);
+ if (charCode > 255) {
+ _free(ptr);
+ throwBindingError('String has UTF-16 code units that do not fit in 8 bits');
+ }
+ HEAPU8[ptr + 4 + i] = charCode;
+ }
+ if (destructors !== null) {
+ destructors.push(_free, ptr);
+ }
+ return ptr;
+ },
+ destructorFunction: function(ptr) { _free(ptr); },
+ });
+}
+
+function __embind_register_std_wstring(rawType, charSize, name) {
+ name = readLatin1String(name);
+ var HEAP, shift;
+ if (charSize === 2) {
+ HEAP = HEAPU16;
+ shift = 1;
+ } else if (charSize === 4) {
+ HEAP = HEAPU32;
+ shift = 2;
+ }
+ registerType(rawType, {
+ name: name,
+ 'fromWireType': function(value) {
+ var length = HEAPU32[value >> 2];
+ var a = new Array(length);
+ var start = (value + 4) >> shift;
+ for (var i = 0; i < length; ++i) {
+ a[i] = String.fromCharCode(HEAP[start + i]);
+ }
+ _free(value);
+ return a.join('');
+ },
+ 'toWireType': function(destructors, value) {
+ // assumes 4-byte alignment
+ var length = value.length;
+ var ptr = _malloc(4 + length * charSize);
+ HEAPU32[ptr >> 2] = length;
+ var start = (ptr + 4) >> shift;
for (var i = 0; i < length; ++i) {
- HEAPU8[ptr + 4 + i] = value.charCodeAt(i);
+ HEAP[start + i] = value.charCodeAt(i);
+ }
+ if (destructors !== null) {
+ destructors.push(_free, ptr);
}
- destructors.push(_free, ptr);
return ptr;
},
+ destructorFunction: function(ptr) { _free(ptr); },
});
}
function __embind_register_emval(rawType, name) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
registerType(rawType, {
name: name,
- fromWireType: function(handle) {
+ 'fromWireType': function(handle) {
var rv = _emval_handle_array[handle].value;
__emval_decref(handle);
return rv;
},
- toWireType: function(destructors, value) {
+ 'toWireType': function(destructors, value) {
return __emval_register(value);
},
+ destructorFunction: null, // This type does not need a destructor
});
}
@@ -367,30 +457,142 @@ function runDestructors(destructors) {
}
}
-function makeInvoker(name, argCount, argTypes, invoker, fn) {
- if (!FUNCTION_TABLE[fn]) {
- throwBindingError('function '+name+' is not defined');
+// Function implementation of operator new, per
+// http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
+// 13.2.2
+// ES3
+function new_(constructor, argumentList) {
+ if (!(constructor instanceof Function)) {
+ throw new TypeError('new_ called with constructor type ' + typeof(constructor) + " which is not a function");
+ }
+
+ /*
+ * Previously, the following line was just:
+
+ function dummy() {};
+
+ * Unfortunately, Chrome was preserving 'dummy' as the object's name, even though at creation, the 'dummy' has the
+ * correct constructor name. Thus, objects created with IMVU.new would show up in the debugger as 'dummy', which
+ * isn't very helpful. Using IMVU.createNamedFunction addresses the issue. Doublely-unfortunately, there's no way
+ * to write a test for this behavior. -NRD 2013.02.22
+ */
+ var dummy = createNamedFunction(constructor.name, function(){});
+ dummy.prototype = constructor.prototype;
+ var obj = new dummy;
+
+ var r = constructor.apply(obj, argumentList);
+ return (r instanceof Object) ? r : obj;
+}
+
+// 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.
+ // argTypes[1] is the type object for function this object/class type, or null if not crafting an invoker for a class method.
+ // argTypes[2...] are the actual function parameters.
+ // classType: The embind type object for the class to be bound, or null if this is not a method of a class.
+ // cppInvokerFunc: JS Function object to the C++-side function that interops into C++ code.
+ // cppTargetFunc: Function pointer (an integer to FUNCTION_TABLE) to the target C++ function the cppInvokerFunc will end up calling.
+ var argCount = argTypes.length;
+
+ 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!');
+ }
+
+ // 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];
+// }
+
+ var argsList = "";
+ var argsListWired = "";
+ for(var i = 0; i < argCount-2; ++i) {
+ argsList += (i!==0?", ":"")+"arg"+i;
+ argsListWired += (i!==0?", ":"")+"arg"+i+"Wired";
}
- return createNamedFunction(makeLegalFunctionName(name), function() {
- if (arguments.length !== argCount - 1) {
- throwBindingError('function ' + name + ' called with ' + arguments.length + ' arguments, expected ' + (argCount - 1));
+
+ var invokerFnBody =
+ "return function "+makeLegalFunctionName(humanName)+"("+argsList+") {\n" +
+ "if (arguments.length !== "+(argCount - 2)+") {\n" +
+ "throwBindingError('function "+humanName+" called with ' + arguments.length + ' arguments, expected "+(argCount - 2)+" args!');\n" +
+ "}\n";
+
+ // Determine if we need to use a dynamic stack to store the destructors for the function parameters.
+ // TODO: Remove this completely once all function invokers are being dynamically generated.
+ var needsDestructorStack = false;
+
+ for(var i = 1; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here.
+ if (argTypes[i] !== null && argTypes[i].destructorFunction === undefined) { // The type does not define a destructor function - must use dynamic stack
+ needsDestructorStack = true;
+ break;
}
- var destructors = [];
- var args = new Array(argCount);
- args[0] = fn;
- for (var i = 1; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]);
+ }
+
+ if (needsDestructorStack) {
+ invokerFnBody +=
+ "var destructors = [];\n";
+ }
+
+ var dtorStack = needsDestructorStack ? "destructors" : "null";
+ var args1 = ["throwBindingError", "classType", "invoker", "fn", "runDestructors", "retType", "classParam"];
+ var args2 = [throwBindingError, classType, cppInvokerFunc, cppTargetFunc, runDestructors, argTypes[0], argTypes[1]];
+
+ if (isClassMethodFunc) {
+ invokerFnBody += "var thisWired = classParam.toWireType("+dtorStack+", this);\n";
+ }
+
+ for(var i = 0; i < argCount-2; ++i) {
+ invokerFnBody += "var arg"+i+"Wired = argType"+i+".toWireType("+dtorStack+", arg"+i+"); // "+argTypes[i+2].name+"\n";
+ args1.push("argType"+i);
+ args2.push(argTypes[i+2]);
+ }
+
+ if (isClassMethodFunc) {
+ argsListWired = "thisWired" + (argsListWired.length > 0 ? ", " : "") + argsListWired;
+ }
+
+ var returns = (argTypes[0].name !== "void");
+
+ invokerFnBody +=
+ (returns?"var rv = ":"") + "invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";
+
+ if (needsDestructorStack) {
+ invokerFnBody += "runDestructors(destructors);\n";
+ } else {
+ for(var i = isClassMethodFunc?1:2; i < argTypes.length; ++i) { // Skip return value at index 0 - it's not deleted here. Also skip class type if not a method.
+ var paramName = (i === 1 ? "thisWired" : ("arg"+(i-2)+"Wired"));
+ if (argTypes[i].destructorFunction !== null) {
+ invokerFnBody += paramName+"_dtor("+paramName+"); // "+argTypes[i].name+"\n";
+ args1.push(paramName+"_dtor");
+ args2.push(argTypes[i].destructorFunction);
+ }
}
- var rv = invoker.apply(null, args);
- rv = argTypes[0].fromWireType(rv);
- runDestructors(destructors);
- return rv;
- });
+ }
+
+ if (returns) {
+ invokerFnBody += "return retType.fromWireType(rv);\n";
+ }
+ invokerFnBody += "}\n";
+
+ args1.push(invokerFnBody);
+
+ 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 = Pointer_stringify(name);
+ name = readLatin1String(name);
rawInvoker = FUNCTION_TABLE[rawInvoker];
exposePublicSymbol(name, function() {
@@ -398,7 +600,8 @@ function __embind_register_function(name, argCount, rawArgTypesAddr, rawInvoker,
}, argCount - 1);
whenDependentTypesAreResolved([], argTypes, function(argTypes) {
- replacePublicSymbol(name, makeInvoker(name, argCount, argTypes, rawInvoker, fn), argCount - 1);
+ var invokerArgsArray = [argTypes[0] /* return value */, null /* no class 'this'*/].concat(argTypes.slice(1) /* actual params */);
+ replacePublicSymbol(name, craftInvokerFunction(name, invokerArgsArray, null /* no class 'this'*/, rawInvoker, fn), argCount - 1);
return [];
});
}
@@ -407,7 +610,7 @@ var tupleRegistrations = {};
function __embind_register_tuple(rawType, name, rawConstructor, rawDestructor) {
tupleRegistrations[rawType] = {
- name: Pointer_stringify(name),
+ name: readLatin1String(name),
rawConstructor: FUNCTION_TABLE[rawConstructor],
rawDestructor: FUNCTION_TABLE[rawDestructor],
elements: [],
@@ -453,18 +656,18 @@ function __embind_finalize_tuple(rawTupleType) {
var setter = elt.setter;
var setterContext = elt.setterContext;
elt.read = function(ptr) {
- return getterReturnType.fromWireType(getter(getterContext, ptr));
+ return getterReturnType['fromWireType'](getter(getterContext, ptr));
};
elt.write = function(ptr, o) {
var destructors = [];
- setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o));
+ setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o));
runDestructors(destructors);
};
});
return [{
name: reg.name,
- fromWireType: function(ptr) {
+ 'fromWireType': function(ptr) {
var rv = new Array(elementsLength);
for (var i = 0; i < elementsLength; ++i) {
rv[i] = elements[i].read(ptr);
@@ -472,7 +675,7 @@ function __embind_finalize_tuple(rawTupleType) {
rawDestructor(ptr);
return rv;
},
- toWireType: function(destructors, o) {
+ 'toWireType': function(destructors, o) {
if (elementsLength !== o.length) {
throw new TypeError("Incorrect number of tuple elements");
}
@@ -480,9 +683,12 @@ function __embind_finalize_tuple(rawTupleType) {
for (var i = 0; i < elementsLength; ++i) {
elements[i].write(ptr, o[i]);
}
- destructors.push(rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(rawDestructor, ptr);
+ }
return ptr;
},
+ destructorFunction: rawDestructor,
}];
});
}
@@ -496,7 +702,7 @@ function __embind_register_struct(
rawDestructor
) {
structRegistrations[rawType] = {
- name: Pointer_stringify(name),
+ name: readLatin1String(name),
rawConstructor: FUNCTION_TABLE[rawConstructor],
rawDestructor: FUNCTION_TABLE[rawDestructor],
fields: [],
@@ -514,7 +720,7 @@ function __embind_register_struct_field(
setterContext
) {
structRegistrations[structType].fields.push({
- fieldName: Pointer_stringify(fieldName),
+ fieldName: readLatin1String(fieldName),
getterReturnType: getterReturnType,
getter: FUNCTION_TABLE[getter],
getterContext: getterContext,
@@ -545,12 +751,12 @@ function __embind_finalize_struct(structType) {
var setterContext = field.setterContext;
fields[fieldName] = {
read: function(ptr) {
- return getterReturnType.fromWireType(
+ return getterReturnType['fromWireType'](
getter(getterContext, ptr));
},
write: function(ptr, o) {
var destructors = [];
- setter(setterContext, ptr, setterArgumentType.toWireType(destructors, o));
+ setter(setterContext, ptr, setterArgumentType['toWireType'](destructors, o));
runDestructors(destructors);
}
};
@@ -558,7 +764,7 @@ function __embind_finalize_struct(structType) {
return [{
name: reg.name,
- fromWireType: function(ptr) {
+ 'fromWireType': function(ptr) {
var rv = {};
for (var i in fields) {
rv[i] = fields[i].read(ptr);
@@ -566,7 +772,7 @@ function __embind_finalize_struct(structType) {
rawDestructor(ptr);
return rv;
},
- toWireType: function(destructors, o) {
+ 'toWireType': function(destructors, o) {
// todo: Here we have an opportunity for -O3 level "unsafe" optimizations:
// assume all fields are present without checking.
for (var fieldName in fields) {
@@ -578,55 +784,17 @@ function __embind_finalize_struct(structType) {
for (fieldName in fields) {
fields[fieldName].write(ptr, o[fieldName]);
}
- destructors.push(rawDestructor, ptr);
+ if (destructors !== null) {
+ destructors.push(rawDestructor, ptr);
+ }
return ptr;
},
+ destructorFunction: rawDestructor,
}];
});
}
-function RegisteredPointer(
- name,
- registeredClass,
- isReference,
- isConst,
-
- // smart pointer properties
- isSmartPointer,
- pointeeType,
- sharingPolicy,
- rawGetPointee,
- rawConstructor,
- rawShare,
- rawDestructor
-) {
- this.name = name;
- this.registeredClass = registeredClass;
- this.isReference = isReference;
- this.isConst = isConst;
-
- // smart pointer properties
- this.isSmartPointer = isSmartPointer;
- this.pointeeType = pointeeType;
- this.sharingPolicy = sharingPolicy;
- this.rawGetPointee = rawGetPointee;
- this.rawConstructor = rawConstructor;
- this.rawShare = rawShare;
- this.rawDestructor = rawDestructor;
-}
-
-RegisteredPointer.prototype.toWireType = function(destructors, handle) {
- var self = this;
- function throwCannotConvert() {
- var name;
- if (handle.$$.smartPtrType) {
- name = handle.$$.smartPtrType.name;
- } else {
- name = handle.$$.ptrType.name;
- }
- throwBindingError('Cannot convert argument of type ' + name + ' to parameter type ' + self.name);
- }
-
+var genericPointerToWireType = function(destructors, handle) {
if (handle === null) {
if (this.isReference) {
throwBindingError('null is not a valid ' + this.name);
@@ -640,33 +808,39 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) {
return 0;
}
}
- if (!(handle instanceof this.registeredClass.constructor)) {
- throwBindingError('Expected null or instance of ' + this.name + ', got ' + _embind_repr(handle));
+
+ if (!handle.$$) {
+ throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name);
}
- // TODO: this is not strictly true
- // We could support BY_EMVAL conversions from raw pointers to smart pointers
- // because the smart pointer can hold a reference to the handle
- if (this.isSmartPointer && undefined === handle.$$.smartPtr) {
- throwBindingError('Passing raw pointer to smart pointer is illegal');
+ if (!handle.$$.ptr) {
+ throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name);
}
if (!this.isConst && handle.$$.ptrType.isConst) {
- throwCannotConvert();
+ throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name);
}
var handleClass = handle.$$.ptrType.registeredClass;
var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+
if (this.isSmartPointer) {
+ // TODO: this is not strictly true
+ // We could support BY_EMVAL conversions from raw pointers to smart pointers
+ // because the smart pointer can hold a reference to the handle
+ if (undefined === handle.$$.smartPtr) {
+ throwBindingError('Passing raw pointer to smart pointer is illegal');
+ }
+
switch (this.sharingPolicy) {
case 0: // NONE
// no upcasting
if (handle.$$.smartPtrType === this) {
ptr = handle.$$.smartPtr;
} else {
- throwCannotConvert();
+ throwBindingError('Cannot convert argument of type ' + (handle.$$.smartPtrType ? handle.$$.smartPtrType.name : handle.$$.ptrType.name) + ' to parameter type ' + this.name);
}
break;
case 1: // INTRUSIVE
- throwBindingError('INTRUSIVE sharing policy not yet supported');
+ ptr = handle.$$.smartPtr;
break;
case 2: // BY_EMVAL
@@ -691,6 +865,97 @@ RegisteredPointer.prototype.toWireType = function(destructors, handle) {
return ptr;
};
+// If we know a pointer type is not going to have SmartPtr logic in it, we can
+// special-case optimize it a bit (compare to genericPointerToWireType)
+var constNoSmartPtrRawPointerToWireType = function(destructors, handle) {
+ if (handle === null) {
+ if (this.isReference) {
+ throwBindingError('null is not a valid ' + this.name);
+ }
+ return 0;
+ }
+
+ if (!handle.$$) {
+ throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name);
+ }
+ if (!handle.$$.ptr) {
+ throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name);
+ }
+ var handleClass = handle.$$.ptrType.registeredClass;
+ var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+ return ptr;
+};
+
+// An optimized version for non-const method accesses - there we must additionally restrict that
+// the pointer is not a const-pointer.
+var nonConstNoSmartPtrRawPointerToWireType = function(destructors, handle) {
+ if (handle === null) {
+ if (this.isReference) {
+ throwBindingError('null is not a valid ' + this.name);
+ }
+ return 0;
+ }
+
+ if (!handle.$$) {
+ throwBindingError('Cannot pass "' + _embind_repr(handle) + '" as a ' + this.name);
+ }
+ if (!handle.$$.ptr) {
+ throwBindingError('Cannot pass deleted object as a pointer of type ' + this.name);
+ }
+ if (handle.$$.ptrType.isConst) {
+ throwBindingError('Cannot convert argument of type ' + handle.$$.ptrType.name + ' to parameter type ' + this.name);
+ }
+ var handleClass = handle.$$.ptrType.registeredClass;
+ var ptr = upcastPointer(handle.$$.ptr, handleClass, this.registeredClass);
+ return ptr;
+};
+
+function RegisteredPointer(
+ name,
+ registeredClass,
+ isReference,
+ isConst,
+
+ // smart pointer properties
+ isSmartPointer,
+ pointeeType,
+ sharingPolicy,
+ rawGetPointee,
+ rawConstructor,
+ rawShare,
+ rawDestructor
+) {
+ this.name = name;
+ this.registeredClass = registeredClass;
+ this.isReference = isReference;
+ this.isConst = isConst;
+
+ // smart pointer properties
+ this.isSmartPointer = isSmartPointer;
+ this.pointeeType = pointeeType;
+ this.sharingPolicy = sharingPolicy;
+ this.rawGetPointee = rawGetPointee;
+ this.rawConstructor = rawConstructor;
+ this.rawShare = rawShare;
+ this.rawDestructor = rawDestructor;
+
+ if (!isSmartPointer && registeredClass.baseClass === undefined) {
+ if (isConst) {
+ this['toWireType'] = constNoSmartPtrRawPointerToWireType;
+ this.destructorFunction = null;
+ } else {
+ this['toWireType'] = nonConstNoSmartPtrRawPointerToWireType;
+ this.destructorFunction = null;
+ }
+ } else {
+ this['toWireType'] = genericPointerToWireType;
+ // Here we must leave this.destructorFunction undefined, since whether genericPointerToWireType returns
+ // a pointer that needs to be freed up is runtime-dependent, and cannot be evaluated at registration time.
+ // TODO: Create an alternative mechanism that allows removing the use of var destructors = []; array in
+ // craftInvokerFunction altogether.
+ }
+}
+
RegisteredPointer.prototype.getPointee = function(ptr) {
if (this.rawGetPointee) {
ptr = this.rawGetPointee(ptr);
@@ -704,7 +969,7 @@ RegisteredPointer.prototype.destructor = function(ptr) {
}
};
-RegisteredPointer.prototype.fromWireType = function(ptr) {
+RegisteredPointer.prototype['fromWireType'] = function(ptr) {
// ptr is a raw pointer (or a raw smartpointer)
// rawPointer is a maybe-null raw pointer
@@ -775,7 +1040,7 @@ function makeClassHandle(prototype, record) {
}
record.count = { value: 1 };
return Object.create(prototype, {
- '$$': {
+ $$: {
value: record,
},
});
@@ -795,7 +1060,7 @@ ClassHandle.prototype.clone = function() {
}
var clone = Object.create(Object.getPrototypeOf(this), {
- '$$': {
+ $$: {
value: shallowCopy(this.$$),
}
});
@@ -865,7 +1130,7 @@ function __embind_register_class(
name,
rawDestructor
) {
- name = Pointer_stringify(name);
+ name = readLatin1String(name);
rawDestructor = FUNCTION_TABLE[rawDestructor];
getActualType = FUNCTION_TABLE[getActualType];
upcast = FUNCTION_TABLE[upcast];
@@ -988,13 +1253,13 @@ function __embind_register_class_constructor(
var args = new Array(argCount);
args[0] = rawConstructor;
for (var i = 1; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i - 1]);
+ args[i] = argTypes[i]['toWireType'](destructors, arguments[i - 1]);
}
var ptr = invoker.apply(null, args);
runDestructors(destructors);
- return argTypes[0].fromWireType(ptr);
+ return argTypes[0]['fromWireType'](ptr);
};
return [];
});
@@ -1016,6 +1281,9 @@ function downcastPointer(ptr, ptrClass, desiredClass) {
function upcastPointer(ptr, ptrClass, desiredClass) {
while (ptrClass !== desiredClass) {
+ if (!ptrClass.upcast) {
+ throwBindingError("Expected null or instance of " + desiredClass.name + ", got an instance of " + ptrClass.name);
+ }
ptr = ptrClass.upcast(ptr);
ptrClass = ptrClass.baseClass;
}
@@ -1049,7 +1317,7 @@ function __embind_register_class_function(
context
) {
var rawArgTypes = heap32VectorToArray(argCount, rawArgTypesAddr);
- methodName = Pointer_stringify(methodName);
+ methodName = readLatin1String(methodName);
rawInvoker = FUNCTION_TABLE[rawInvoker];
whenDependentTypesAreResolved([], [rawClassType], function(classType) {
@@ -1057,8 +1325,8 @@ function __embind_register_class_function(
var humanName = classType.name + '.' + methodName;
var unboundTypesHandler = function() {
- throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
- };
+ throwUnboundTypeError('Cannot call ' + humanName + ' due to unbound types', rawArgTypes);
+ };
var proto = classType.registeredClass.instancePrototype;
var method = proto[methodName];
@@ -1074,25 +1342,8 @@ function __embind_register_class_function(
}
whenDependentTypesAreResolved([], rawArgTypes, function(argTypes) {
- var memberFunction = createNamedFunction(makeLegalFunctionName(humanName), function() {
- if (arguments.length !== argCount - 2) {
- throwBindingError(humanName + ' called with ' + arguments.length + ' arguments, expected ' + (argCount-2));
- }
-
- validateThis(this, classType, humanName);
-
- var destructors = [];
- var args = new Array(argCount + 1);
- args[0] = context;
- args[1] = argTypes[1].toWireType(destructors, this);
- for (var i = 2; i < argCount; ++i) {
- args[i] = argTypes[i].toWireType(destructors, arguments[i - 2]);
- }
- var rv = rawInvoker.apply(null, args);
- rv = argTypes[0].fromWireType(rv);
- runDestructors(destructors);
- return rv;
- });
+
+ var memberFunction = craftInvokerFunction(humanName, argTypes, classType, rawInvoker, context);
// Replace the initial unbou