summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/cases/floatundefinvoke_fastcomp.ll30
-rw-r--r--tests/cases/floatundefinvoke_fastcomp.txt3
-rw-r--r--tests/cases/i1tof_ta2.ll71
-rw-r--r--tests/cases/i1tof_ta2.txt3
-rw-r--r--tests/core/test_double_varargs.c34
-rw-r--r--tests/core/test_double_varargs.out2
-rw-r--r--tests/core/test_exceptions_white_list_empty.out0
-rw-r--r--tests/embind/embind.test.js424
-rw-r--r--tests/embind/embind_test.cpp285
-rw-r--r--tests/emscripten_fs_api_browser.cpp5
-rw-r--r--tests/life.c2
-rw-r--r--tests/sdl_audio_mix.c2
-rw-r--r--tests/test_benchmark.py37
-rw-r--r--tests/test_core.py98
-rw-r--r--tests/test_other.py102
-rw-r--r--tests/test_sanity.py2
-rw-r--r--tests/webidl/output.txt2
-rw-r--r--tests/webidl/post.js3
-rw-r--r--tests/webidl/test.h3
-rw-r--r--tests/webidl/test.idl3
20 files changed, 1071 insertions, 40 deletions
diff --git a/tests/cases/floatundefinvoke_fastcomp.ll b/tests/cases/floatundefinvoke_fastcomp.ll
new file mode 100644
index 00000000..215506ef
--- /dev/null
+++ b/tests/cases/floatundefinvoke_fastcomp.ll
@@ -0,0 +1,30 @@
+; ModuleID = 'a.o'
+target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32-S128"
+target triple = "asmjs-unknown-emscripten"
+
+@.str = private unnamed_addr constant [11 x i8] c"float: %f\0A\00", align 1
+
+define void @_Z10printFloatf(float %f) #0 {
+entry:
+ %conv = fpext float %f to double
+ %call = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), double %conv)
+ ret void
+}
+
+define i32 @main() #1 {
+entry:
+ tail call void @_Z10printFloatf(float 1.000000e+00)
+ call void @emscripten_preinvoke()
+ call void @_Z10printFloatf(float undef)
+ %last = call i32 @emscripten_postinvoke()
+ %lastf = sitofp i32 %last to float
+ tail call void @_Z10printFloatf(float %lastf)
+ ret i32 1
+}
+
+declare void @emscripten_preinvoke()
+declare i32 @emscripten_postinvoke()
+declare i32 @printf(i8* nocapture, ...) #1
+
+attributes #0 = { noinline nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }
diff --git a/tests/cases/floatundefinvoke_fastcomp.txt b/tests/cases/floatundefinvoke_fastcomp.txt
new file mode 100644
index 00000000..5e19391e
--- /dev/null
+++ b/tests/cases/floatundefinvoke_fastcomp.txt
@@ -0,0 +1,3 @@
+float: 1.000000
+float: 0.000000
+float: 0.000000
diff --git a/tests/cases/i1tof_ta2.ll b/tests/cases/i1tof_ta2.ll
new file mode 100644
index 00000000..12940907
--- /dev/null
+++ b/tests/cases/i1tof_ta2.ll
@@ -0,0 +1,71 @@
+; ModuleID = 'bad/emcc-0-basebc.bc'
+target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32-S128"
+target triple = "asmjs-unknown-emscripten"
+
+@.str = private unnamed_addr constant [7 x i8] c"%0.1f\0A\00", align 1
+@.str2 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
+
+; Function Attrs: noinline
+define float @_Z2v1v() #0 {
+entry:
+ %call = tail call double @emscripten_get_now()
+ %cmp = fcmp oge double %call, 0.000000e+00
+ %cond = select i1 %cmp, float 0x3FECCCCCC0000000, float 0.000000e+00
+ ret float %cond
+}
+
+define double @emscripten_get_now() #0 {
+ ret double 1.0
+}
+
+; Function Attrs: noinline
+define float @_Z2v2v() #0 {
+entry:
+ %call = tail call double @emscripten_get_now()
+ %cmp = fcmp oge double %call, 0.000000e+00
+ %cond = select i1 %cmp, float 0x3FD99999A0000000, float 0.000000e+00
+ ret float %cond
+}
+
+; Function Attrs: noinline
+define float @_Z2v3v() #0 {
+entry:
+ %call = tail call double @emscripten_get_now()
+ %cmp = fcmp oge double %call, 0.000000e+00
+ %cond = select i1 %cmp, float 0x3FB99999A0000000, float 0.000000e+00
+ ret float %cond
+}
+
+define i32 @main() #1 {
+entry:
+ %call = tail call float @_Z2v1v()
+ %call1 = tail call float @_Z2v2v()
+ %call2 = tail call float @_Z2v3v()
+ %sub = fsub float %call1, %call
+ %cmp.i = fcmp ogt float %sub, 0.000000e+00
+ br i1 %cmp.i, label %_ZL5signff.exit, label %cond.false.i
+
+cond.false.i: ; preds = %entry
+ %cmp1.i = fcmp olt float %sub, 0.000000e+00
+ %phitmp = sitofp i1 %cmp1.i to float
+ %phitmpd = fpext float %phitmp to double
+ %call1115a = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([7 x i8]* @.str, i32 0, i32 0), double %phitmpd)
+ %phitmpi = sext i1 %cmp1.i to i32
+ %call1115b = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str2, i32 0, i32 0), i32 %phitmpi)
+ br label %_ZL5signff.exit
+
+_ZL5signff.exit: ; preds = %cond.false.i, %entry
+ %cond2.i = phi float [ %phitmp, %cond.false.i ], [ 1.000000e+00, %entry ]
+ %mul = fmul float %call2, %cond2.i
+ %add = fadd float %call, %mul
+ %conv4 = fpext float %add to double
+ %call5 = tail call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([7 x i8]* @.str, i32 0, i32 0), double %conv4)
+ ret i32 0
+}
+
+; Function Attrs: nounwind
+declare i32 @printf(i8* nocapture, ...) #2
+
+attributes #0 = { noinline "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #2 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf"="true" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="false" "use-soft-float"="false" }
diff --git a/tests/cases/i1tof_ta2.txt b/tests/cases/i1tof_ta2.txt
new file mode 100644
index 00000000..5d3943cd
--- /dev/null
+++ b/tests/cases/i1tof_ta2.txt
@@ -0,0 +1,3 @@
+-1.0
+-1
+0.8
diff --git a/tests/core/test_double_varargs.c b/tests/core/test_double_varargs.c
new file mode 100644
index 00000000..d3eb3eed
--- /dev/null
+++ b/tests/core/test_double_varargs.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdarg.h>
+
+double func_int_double_1(int unused1, ...)
+{
+ int i;
+ double d;
+ va_list vl;
+ va_start(vl, unused1);
+ i = va_arg(vl, int);
+ d = va_arg(vl, double);
+ va_end(vl);
+ return i+d;
+}
+
+double func_int_double_2(int unused1, int unused2, ...)
+{
+ int i;
+ double d;
+ va_list vl;
+ va_start(vl, unused2);
+ i = va_arg(vl, int);
+ d = va_arg(vl, double);
+ va_end(vl);
+ return i+d;
+}
+
+int main() {
+ double ret = func_int_double_1(0, 5, 10.0);
+ printf("%f\n", ret); // Expects to print 15
+ ret = func_int_double_2(0, 0, 5, 10.0);
+ printf("%f\n", ret); // Expects to print 15
+}
+
diff --git a/tests/core/test_double_varargs.out b/tests/core/test_double_varargs.out
new file mode 100644
index 00000000..c907dece
--- /dev/null
+++ b/tests/core/test_double_varargs.out
@@ -0,0 +1,2 @@
+15.000000
+15.000000
diff --git a/tests/core/test_exceptions_white_list_empty.out b/tests/core/test_exceptions_white_list_empty.out
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/core/test_exceptions_white_list_empty.out
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js
index 3ded811a..a6b2e98c 100644
--- a/tests/embind/embind.test.js
+++ b/tests/embind/embind.test.js
@@ -1548,7 +1548,7 @@ module({
});
});
- BaseFixture.extend("abstract methods", function() {
+ BaseFixture.extend("implementing abstract methods with JS objects", function() {
test("can call abstract methods", function() {
var obj = cm.getAbstractClass();
assert.equal("from concrete", obj.abstractMethod());
@@ -1580,7 +1580,8 @@ module({
};
var impl = cm.AbstractClass.implement(new MyImplementation);
- assert.equal(expected, impl.optionalMethod(expected));
+ // TODO: remove .implement() as a public API. It interacts poorly with Class.extend.
+ //assert.equal(expected, impl.optionalMethod(expected));
assert.equal(expected, cm.callOptionalMethod(impl, expected));
impl.delete();
});
@@ -1588,7 +1589,8 @@ module({
test("if not implemented then optional method runs default", function() {
var impl = cm.AbstractClass.implement({});
assert.equal("optionalfoo", impl.optionalMethod("foo"));
- assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
+ // TODO: remove .implement() as a public API. It interacts poorly with Class.extend.
+ //assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
impl.delete();
});
@@ -1638,6 +1640,366 @@ module({
impl.delete();
});
+
+ test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr();
+ var impl = cm.AbstractClass.implement({
+ returnsSharedPtr: function() {
+ return derived;
+ }
+ });
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ derived.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+ });
+
+ BaseFixture.extend("constructor prototype class inheritance", function() {
+ var Empty = cm.AbstractClass.extend("Empty", {
+ abstractMethod: function() {
+ }
+ });
+
+ test("can extend, construct, and delete", function() {
+ var instance = new Empty;
+ instance.delete();
+ });
+
+ test("properties set in constructor are externally visible", function() {
+ var HasProperty = cm.AbstractClass.extend("HasProperty", {
+ __construct: function(x) {
+ this.__parent.__construct.call(this);
+ this.property = x;
+ },
+ abstractMethod: function() {
+ }
+ });
+ var instance = new HasProperty(10);
+ assert.equal(10, instance.property);
+ instance.delete();
+ });
+
+ test("pass derived object to c++", function() {
+ var Implementation = cm.AbstractClass.extend("Implementation", {
+ abstractMethod: function() {
+ return "abc";
+ },
+ });
+ var instance = new Implementation;
+ var result = cm.callAbstractMethod(instance);
+ instance.delete();
+ assert.equal("abc", result);
+ });
+
+ test("properties set in constructor are visible in overridden methods", function() {
+ var HasProperty = cm.AbstractClass.extend("HasProperty", {
+ __construct: function(x) {
+ this.__parent.__construct.call(this);
+ this.x = x;
+ },
+ abstractMethod: function() {
+ return this.x;
+ },
+ });
+ var instance = new HasProperty("xyz");
+ var result = cm.callAbstractMethod(instance);
+ instance.delete();
+ assert.equal("xyz", result);
+ });
+
+ test("interface methods are externally visible", function() {
+ var instance = new Empty;
+ var result = instance.concreteMethod();
+ instance.delete();
+ assert.equal("concrete", result);
+ });
+
+ test("optional methods are externally visible", function() {
+ var instance = new Empty;
+ var result = instance.optionalMethod("_123");
+ instance.delete();
+ assert.equal("optional_123", result);
+ });
+
+ test("optional methods: not defined", function() {
+ var instance = new Empty;
+ var result = cm.callOptionalMethod(instance, "_123");
+ instance.delete();
+ assert.equal("optional_123", result);
+ });
+
+ // Calling C++ implementations of optional functions can be
+ // made to work, but requires an interface change on the C++
+ // side, using a technique similar to the one described at
+ // https://wiki.python.org/moin/boost.python/OverridableVirtualFunctions
+ //
+ // The issue is that, in a standard binding, calling
+ // parent.prototype.optionalMethod invokes the wrapper
+ // function, which checks that the JS object implements
+ // 'optionalMethod', which it does. Thus, C++ calls back into
+ // JS, resulting in an infinite loop.
+ //
+ // The solution, for optional methods, is to bind a special
+ // concrete implementation that specifically calls the base
+ // class's implementation. See the binding of
+ // AbstractClass::optionalMethod in embind_test.cpp.
+
+ test("can call parent implementation from within derived implementation", function() {
+ var parent = cm.AbstractClass;
+ var ExtendsOptionalMethod = parent.extend("ExtendsOptionalMethod", {
+ abstractMethod: function() {
+ },
+ optionalMethod: function(s) {
+ return "optionaljs_" + parent.prototype.optionalMethod.call(this, s);
+ },
+ });
+ var instance = new ExtendsOptionalMethod;
+ var result = cm.callOptionalMethod(instance, "_123");
+ instance.delete();
+ assert.equal("optionaljs_optional_123", result);
+ });
+
+ // TODO: deriving from classes with constructors?
+
+ test("instanceof", function() {
+ var instance = new Empty;
+ assert.instanceof(instance, Empty);
+ assert.instanceof(instance, cm.AbstractClass);
+ instance.delete();
+ });
+
+ test("returning null shared pointer from interfaces implemented in JS code does not leak", function() {
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ returnsSharedPtr: function() {
+ return null;
+ }
+ });
+ var impl = new C;
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+
+ test("returning a new shared pointer from interfaces implemented in JS code does not leak", function() {
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ returnsSharedPtr: function() {
+ return cm.embind_test_return_smart_derived_ptr().deleteLater();
+ }
+ });
+ var impl = new C;
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+
+ test("void methods work", function() {
+ var saved = {};
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ differentArguments: function(i, d, f, q, s) {
+ saved.i = i;
+ saved.d = d;
+ saved.f = f;
+ saved.q = q;
+ saved.s = s;
+ }
+ });
+ var impl = new C;
+
+ cm.callDifferentArguments(impl, 1, 2, 3, 4, "foo");
+
+ assert.deepEqual(saved, {
+ i: 1,
+ d: 2,
+ f: 3,
+ q: 4,
+ s: "foo",
+ });
+
+ impl.delete();
+ });
+
+ test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
+ var derived = cm.embind_test_return_smart_derived_ptr();
+ var C = cm.AbstractClass.extend("C", {
+ abstractMethod: function() {
+ },
+ returnsSharedPtr: function() {
+ return derived;
+ }
+ });
+ var impl = new C;
+ cm.callReturnsSharedPtrMethod(impl);
+ impl.delete();
+ derived.delete();
+ // Let the memory leak test superfixture check that no leaks occurred.
+ });
+
+ test("calling pure virtual function gives good error message", function() {
+ var C = cm.AbstractClass.extend("C", {});
+ var error = assert.throws(cm.PureVirtualError, function() {
+ new C;
+ });
+ assert.equal('Pure virtual function abstractMethod must be implemented in JavaScript', error.message);
+ });
+
+ test("can extend from C++ class with constructor arguments", function() {
+ var parent = cm.AbstractClassWithConstructor;
+ var C = parent.extend("C", {
+ __construct: function(x) {
+ this.__parent.__construct.call(this, x);
+ },
+ abstractMethod: function() {
+ return this.concreteMethod();
+ }
+ });
+
+ var impl = new C("hi");
+ var rv = cm.callAbstractMethod2(impl);
+ impl.delete();
+
+ assert.equal("hi", rv);
+ });
+
+ test("__destruct is called when object is destroyed", function() {
+ var parent = cm.HeldAbstractClass;
+ var calls = [];
+ var C = parent.extend("C", {
+ method: function() {
+ },
+ __destruct: function() {
+ calls.push("__destruct");
+ this.__parent.__destruct.call(this);
+ }
+ });
+ var impl = new C;
+ var copy = impl.clone();
+ impl.delete();
+ assert.deepEqual([], calls);
+ copy.delete();
+ assert.deepEqual(["__destruct"], calls);
+ });
+
+ test("if JavaScript implementation of interface is returned, don't wrap in new handle", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ method: function() {
+ }
+ });
+ var impl = new C;
+ var rv = cm.passHeldAbstractClass(impl);
+ impl.delete();
+ assert.equal(impl, rv);
+ rv.delete();
+ });
+
+ test("can instantiate two wrappers with constructors", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ __construct: function() {
+ this.__parent.__construct.call(this);
+ },
+ method: function() {
+ }
+ });
+ var a = new C;
+ var b = new C;
+ a.delete();
+ b.delete();
+ });
+
+ test("incorrectly calling parent is an error", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ __construct: function() {
+ this.__parent.__construct();
+ },
+ method: function() {
+ }
+ });
+ assert.throws(cm.BindingError, function() {
+ new C;
+ });
+ });
+
+ test("deleteLater() works for JavaScript implementations", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ method: function() {
+ }
+ });
+ var impl = new C;
+ var rv = cm.passHeldAbstractClass(impl);
+ impl.deleteLater();
+ rv.deleteLater();
+ cm.flushPendingDeletes();
+ });
+
+ test("deleteLater() combined with delete() works for JavaScript implementations", function() {
+ var parent = cm.HeldAbstractClass;
+ var C = parent.extend("C", {
+ method: function() {
+ }
+ });
+ var impl = new C;
+ var rv = cm.passHeldAbstractClass(impl);
+ impl.deleteLater();
+ rv.delete();
+ cm.flushPendingDeletes();
+ });
+
+ test("method arguments with pointer ownership semantics are cleaned up after call", function() {
+ var parent = cm.AbstractClass;
+ var C = parent.extend("C", {
+ abstractMethod: function() {
+ },
+ });
+ var impl = new C;
+ cm.passShared(impl);
+ impl.delete();
+ });
+
+ test("method arguments with pointer ownership semantics can be cloned", function() {
+ var parent = cm.AbstractClass;
+ var owned;
+ var C = parent.extend("C", {
+ abstractMethod: function() {
+ },
+ passShared: function(p) {
+ owned = p.clone();
+ }
+ });
+ var impl = new C;
+ cm.passShared(impl);
+ impl.delete();
+
+ assert.equal("Derived", owned.getClassName());
+ owned.delete();
+ });
+
+ test("emscripten::val method arguments don't leak", function() {
+ var parent = cm.AbstractClass;
+ var got;
+ var C = parent.extend("C", {
+ abstractMethod: function() {
+ },
+ passVal: function(g) {
+ got = g;
+ }
+ });
+ var impl = new C;
+ var v = {};
+ cm.passVal(impl, v);
+ impl.delete();
+
+ assert.equal(v, got);
+ });
});
BaseFixture.extend("registration order", function() {
@@ -1945,19 +2307,6 @@ module({
});
});
- test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
- var derived = cm.embind_test_return_smart_derived_ptr();
- var impl = cm.AbstractClass.implement({
- returnsSharedPtr: function() {
- return derived;
- }
- });
- cm.callReturnsSharedPtrMethod(impl);
- impl.delete();
- derived.delete();
- // Let the memory leak test superfixture check that no leaks occurred.
- });
-
BaseFixture.extend("val::as", function() {
test("built-ins", function() {
assert.equal(true, cm.val_as_bool(true));
@@ -2030,6 +2379,49 @@ module({
assert.equal(65538, instance.c);
});
});
+
+ BaseFixture.extend("intrusive pointers", function() {
+ test("can pass intrusive pointers", function() {
+ var ic = new cm.IntrusiveClass;
+ var d = cm.passThroughIntrusiveClass(ic);
+ assert.true(ic.isAliasOf(d));
+ ic.delete();
+ d.delete();
+ });
+
+ test("can hold intrusive pointers", function() {
+ var ic = new cm.IntrusiveClass;
+ var holder = new cm.IntrusiveClassHolder;
+ holder.set(ic);
+ ic.delete();
+ var d = holder.get();
+ d.delete();
+ holder.delete();
+ });
+
+ test("can extend from intrusive pointer class and still preserve reference in JavaScript", function() {
+ var C = cm.IntrusiveClass.extend("C", {
+ });
+ var instance = new C;
+ var holder = new cm.IntrusiveClassHolder;
+ holder.set(instance);
+ instance.delete();
+
+ var back = holder.get();
+ assert.equal(back, instance);
+ holder.delete();
+ });
+ });
+
+ BaseFixture.extend("typeof", function() {
+ test("typeof", function() {
+ assert.equal("object", cm.getTypeOfVal(null));
+ assert.equal("object", cm.getTypeOfVal({}));
+ assert.equal("function", cm.getTypeOfVal(function(){}));
+ assert.equal("number", cm.getTypeOfVal(1));
+ assert.equal("string", cm.getTypeOfVal("hi"));
+ });
+ });
});
/* global run_all_tests */
diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp
index 5a83903a..30267994 100644
--- a/tests/embind/embind_test.cpp
+++ b/tests/embind/embind_test.cpp
@@ -1090,6 +1090,16 @@ public:
virtual std::shared_ptr<Derived> returnsSharedPtr() = 0;
virtual void differentArguments(int i, double d, unsigned char f, double q, std::string) = 0;
+
+ std::string concreteMethod() const {
+ return "concrete";
+ }
+
+ virtual void passShared(const std::shared_ptr<Derived>&) {
+ }
+
+ virtual void passVal(const val& v) {
+ }
};
EMSCRIPTEN_SYMBOL(optionalMethod);
@@ -1103,9 +1113,10 @@ public:
}
std::string optionalMethod(std::string s) const {
- return optional_call<std::string>(optionalMethod_symbol, [&] {
- return AbstractClass::optionalMethod(s);
- }, s);
+ return call<std::string>("optionalMethod", s);
+ //return optional_call<std::string>(optionalMethod_symbol, [&] {
+ // return AbstractClass::optionalMethod(s);
+ //}, s);
}
std::shared_ptr<Derived> returnsSharedPtr() {
@@ -1115,6 +1126,14 @@ public:
void differentArguments(int i, double d, unsigned char f, double q, std::string s) {
return call<void>("differentArguments", i, d, f, q, s);
}
+
+ virtual void passShared(const std::shared_ptr<Derived>& p) override {
+ return call<void>("passShared", p);
+ }
+
+ virtual void passVal(const val& v) override {
+ return call<void>("passVal", v);
+ }
};
class ConcreteClass : public AbstractClass {
@@ -1122,7 +1141,6 @@ class ConcreteClass : public AbstractClass {
return "from concrete";
}
-
void differentArguments(int i, double d, unsigned char f, double q, std::string s) {
}
@@ -1152,12 +1170,74 @@ void callDifferentArguments(AbstractClass& ac, int i, double d, unsigned char f,
return ac.differentArguments(i, d, f, q, s);
}
+struct AbstractClassWithConstructor {
+ explicit AbstractClassWithConstructor(std::string s)
+ : s(s)
+ {}
+
+ virtual std::string abstractMethod() = 0;
+ std::string concreteMethod() {
+ return s;
+ }
+
+ std::string s;
+};
+
+struct AbstractClassWithConstructorWrapper : public wrapper<AbstractClassWithConstructor> {
+ EMSCRIPTEN_WRAPPER(AbstractClassWithConstructorWrapper);
+
+ virtual std::string abstractMethod() override {
+ return call<std::string>("abstractMethod");
+ }
+};
+
+std::string callAbstractMethod2(AbstractClassWithConstructor& ac) {
+ return ac.abstractMethod();
+}
+
+struct HeldAbstractClass : public PolyBase, public PolySecondBase {
+ virtual void method() = 0;
+};
+struct HeldAbstractClassWrapper : wrapper<HeldAbstractClass> {
+ EMSCRIPTEN_WRAPPER(HeldAbstractClassWrapper);
+
+ virtual void method() override {
+ return call<void>("method");
+ }
+};
+
+std::shared_ptr<PolySecondBase> passHeldAbstractClass(std::shared_ptr<HeldAbstractClass> p) {
+ return p;
+}
+
+void passShared(AbstractClass& ac) {
+ auto p = std::make_shared<Derived>();
+ ac.passShared(p);
+}
+
+void passVal(AbstractClass& ac, val v) {
+ return ac.passVal(v);
+}
+
EMSCRIPTEN_BINDINGS(interface_tests) {
class_<AbstractClass>("AbstractClass")
.smart_ptr<std::shared_ptr<AbstractClass>>("shared_ptr<AbstractClass>")
.allow_subclass<AbstractClassWrapper>("AbstractClassWrapper")
- .function("abstractMethod", &AbstractClass::abstractMethod)
- .function("optionalMethod", &AbstractClass::optionalMethod)
+ .function("abstractMethod", &AbstractClass::abstractMethod, pure_virtual())
+ // The select_overload is necessary because, otherwise, the C++ compiler
+ // cannot deduce the signature of the lambda function.
+ .function("optionalMethod", optional_override(
+ [](AbstractClass& this_, std::string s) {
+ return this_.AbstractClass::optionalMethod(s);
+ }
+ ))
+ .function("concreteMethod", &AbstractClass::concreteMethod)
+ .function("passShared", select_overload<void(AbstractClass&, const std::shared_ptr<Derived>&)>([](AbstractClass& self, const std::shared_ptr<Derived>& derived) {
+ self.AbstractClass::passShared(derived);
+ }))
+ .function("passVal", select_overload<void(AbstractClass&, const val&)>([](AbstractClass& self, const val& v) {
+ self.AbstractClass::passVal(v);
+ }))
;
function("getAbstractClass", &getAbstractClass);
@@ -1165,6 +1245,22 @@ EMSCRIPTEN_BINDINGS(interface_tests) {
function("callOptionalMethod", &callOptionalMethod);
function("callReturnsSharedPtrMethod", &callReturnsSharedPtrMethod);
function("callDifferentArguments", &callDifferentArguments);
+ function("passShared", &passShared);
+ function("passVal", &passVal);
+
+ class_<AbstractClassWithConstructor>("AbstractClassWithConstructor")
+ .allow_subclass<AbstractClassWithConstructorWrapper>("AbstractClassWithConstructorWrapper", constructor<std::string>())
+ .function("abstractMethod", &AbstractClassWithConstructor::abstractMethod, pure_virtual())
+ .function("concreteMethod", &AbstractClassWithConstructor::concreteMethod)
+ ;
+ function("callAbstractMethod2", &callAbstractMethod2);
+
+ class_<HeldAbstractClass, base<PolySecondBase>>("HeldAbstractClass")
+ .smart_ptr<std::shared_ptr<HeldAbstractClass>>("shared_ptr<HeldAbstractClass>")
+ .allow_subclass<HeldAbstractClassWrapper, std::shared_ptr<HeldAbstractClassWrapper>>("HeldAbstractClassWrapper")
+ .function("method", &HeldAbstractClass::method, pure_virtual())
+ ;
+ function("passHeldAbstractClass", &passHeldAbstractClass);
}
template<typename T, size_t sizeOfArray>
@@ -2368,3 +2464,180 @@ EMSCRIPTEN_BINDINGS(val_new_) {
function("construct_with_memory_view", &construct_with_memory_view);
function("construct_with_ints_and_float", &construct_with_ints_and_float);
}
+
+template <typename T>
+class intrusive_ptr {
+public:
+ typedef T element_type;
+
+ intrusive_ptr(std::nullptr_t = nullptr)
+ : px(nullptr)
+ {}
+
+ template <typename U>
+ explicit intrusive_ptr(U* px)
+ : px(px)
+ {
+ addRef(px);
+ }
+
+ intrusive_ptr(const intrusive_ptr& that)
+ : px(that.px)
+ {
+ addRef(px);
+ }
+
+ template<typename U>
+ intrusive_ptr(const intrusive_ptr<U>& that)
+ : px(that.get())
+ {
+ addRef(px);
+ }
+
+ intrusive_ptr& operator=(const intrusive_ptr& that) {
+ reset(that.get());
+ return *this;
+ }
+
+ intrusive_ptr& operator=(intrusive_ptr&& that) {
+ release(px);
+ px = that.px;
+ that.px = 0;
+ return *this;
+ }
+
+ template<typename U>
+ intrusive_ptr& operator=(const intrusive_ptr<U>& that) {
+ reset(that.get());
+ return *this;
+ }
+
+ template<typename U>
+ intrusive_ptr& operator=(intrusive_ptr<U>&& that) {
+ release(px);
+ px = that.px;
+ that.px = 0;
+ return *this;
+ }
+
+ ~intrusive_ptr() {
+ release(px);
+ }
+
+ void reset(T* nx = nullptr) {
+ addRef(nx);
+ release(px);
+ px = nx;
+ }
+
+ T* get() const {
+ return px;
+ }
+
+ T& operator*() const {
+ return *px;
+ }
+
+ T* operator->() const {
+ return px;
+ }
+
+ explicit operator bool() const {
+ return px != nullptr;
+ }
+
+ void swap(intrusive_ptr& rhs) {
+ std::swap(px, rhs.px);
+ }
+
+private:
+ void addRef(T* px) {
+ if (px) {
+ ++px->referenceCount;
+ }
+ }
+
+ void release(T* px) {
+ if (--px->referenceCount == 0) {
+ delete px;
+ }
+ }
+
+ T* px;
+
+ template<typename U>
+ friend class intrusive_ptr;
+};
+
+namespace emscripten {
+ template<typename T>
+ struct smart_ptr_trait<intrusive_ptr<T>> {
+ typedef intrusive_ptr<T> pointer_type;
+ typedef T element_type;
+
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::INTRUSIVE;
+ }
+
+ static T* get(const intrusive_ptr<T>& p) {
+ return p.get();
+ }
+
+ static intrusive_ptr<T> share(const intrusive_ptr<T>& r, T* ptr) {
+ return intrusive_ptr<T>(ptr);
+ }
+
+ static pointer_type* construct_null() {
+ return new pointer_type;
+ }
+ };
+}
+
+template<typename T>
+intrusive_ptr<T> make_intrusive_ptr() {
+ return intrusive_ptr<T>(new T);
+}
+
+struct IntrusiveClass {
+ virtual ~IntrusiveClass() {}
+ long referenceCount = 0;
+};
+
+struct IntrusiveClassWrapper : public wrapper<IntrusiveClass> {
+ EMSCRIPTEN_WRAPPER(IntrusiveClassWrapper);
+};
+
+template<typename T>
+struct Holder {
+ void set(const T& v) {
+ value = v;
+ }
+ const T& get() const {
+ return value;
+ }
+ T value;
+};
+
+EMSCRIPTEN_BINDINGS(intrusive_pointers) {
+ class_<IntrusiveClass>("IntrusiveClass")
+ .smart_ptr_constructor("intrusive_ptr<IntrusiveClass>", &make_intrusive_ptr<IntrusiveClass>)
+ .allow_subclass<IntrusiveClassWrapper, intrusive_ptr<IntrusiveClassWrapper>>("IntrusiveClassWrapper")
+ ;
+
+ typedef Holder<intrusive_ptr<IntrusiveClass>> IntrusiveClassHolder;
+ class_<IntrusiveClassHolder>("IntrusiveClassHolder")
+ .constructor<>()
+ .function("set", &IntrusiveClassHolder::set)
+ .function("get", &IntrusiveClassHolder::get)
+ ;
+
+ function("passThroughIntrusiveClass", &passThrough<intrusive_ptr<IntrusiveClass>>);
+}
+
+std::string getTypeOfVal(const val& v) {
+ return v.typeof().as<std::string>();
+}
+
+EMSCRIPTEN_BINDINGS(typeof) {
+ function("getTypeOfVal", &getTypeOfVal);
+}
diff --git a/tests/emscripten_fs_api_browser.cpp b/tests/emscripten_fs_api_browser.cpp
index 0355287a..1410ba3c 100644
--- a/tests/emscripten_fs_api_browser.cpp
+++ b/tests/emscripten_fs_api_browser.cpp
@@ -107,11 +107,14 @@ int main() {
onLoaded,
onError);
+ char name[40];
+ strcpy(name, "/tmp/screen_shot.png"); // test for issue #2349, name being free'd
emscripten_async_wget(
"http://localhost:8888/screenshot.png",
- "/tmp/screen_shot.png",
+ name,
onLoaded,
onError);
+ memset(name, 0, 30);
emscripten_set_main_loop(wait_wgets, 0, 0);
diff --git a/tests/life.c b/tests/life.c
index 4ce8d385..263fa0e6 100644
--- a/tests/life.c
+++ b/tests/life.c
@@ -67,7 +67,9 @@ void game(int w, int h, int i)
i--;
nudge(univ, w, h); // keep it interesting for benchmark
} else {
+#if !__EMSCRIPTEN__
usleep(20000);
+#endif
show(univ, w, h);
}
}
diff --git a/tests/sdl_audio_mix.c b/tests/sdl_audio_mix.c
index a1c0485d..422fc122 100644
--- a/tests/sdl_audio_mix.c
+++ b/tests/sdl_audio_mix.c
@@ -58,7 +58,9 @@ void one_iter() {
Mix_HaltChannel(soundChannel);
Mix_HaltMusic();
int result = 1;
+#ifdef REPORT_RESULT
REPORT_RESULT();
+#endif
break;
};
}
diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py
index 735f0feb..16abbfdd 100644
--- a/tests/test_benchmark.py
+++ b/tests/test_benchmark.py
@@ -112,6 +112,7 @@ process(sys.argv[1])
'--memory-init-file', '0', '--js-transform', 'python hardcode.py',
'-s', 'TOTAL_MEMORY=128*1024*1024',
#'-profiling',
+ #'--closure', '1',
'-o', final] + shared_args + emcc_args + self.extra_args, stdout=PIPE, stderr=PIPE, env=self.env).communicate()
assert os.path.exists(final), 'Failed to compile file: ' + output[0]
self.filename = final
@@ -407,6 +408,42 @@ class benchmark(RunnerCore):
'''
self.do_benchmark('ifs', src, 'ok', reps=TEST_REPS*5)
+ def test_conditionals(self):
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ int main(int argc, char *argv[]) {
+ int arg = argc > 1 ? argv[1][0] - '0' : 3;
+ switch(arg) {
+ case 0: return 0; break;
+ case 1: arg = 3*75; break;
+ case 2: arg = 3*625; break;
+ case 3: arg = 3*1250; break;
+ case 4: arg = 3*5*1250; break;
+ case 5: arg = 3*10*1250; break;
+ default: printf("error: %d\\n", arg); return -1;
+ }
+
+ int x = 0;
+
+ for (int j = 0; j < 27000; j++) {
+ for (int i = 0; i < arg; i++) {
+ if (((x*x+11) % 3 == 0) | ((x*(x+2)+17) % 5 == 0)) {
+ x += 2;
+ } else {
+ x++;
+ }
+ }
+ }
+
+ printf("ok %d\n", x);
+
+ return x;
+ }
+ '''
+ self.do_benchmark('conditionals', src, 'ok', reps=TEST_REPS*5)
+
def test_fannkuch(self):
src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read().replace(
'int n = argc > 1 ? atoi(argv[1]) : 0;',
diff --git a/tests/test_core.py b/tests/test_core.py
index 6a920a7b..62a061e2 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -425,6 +425,11 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
self.do_run_from_file(src, output, 'waka fleefl asdfasdfasdfasdf'.split(' '))
+ def test_double_varargs(self):
+ test_path = path_from_root('tests', 'core', 'test_double_varargs')
+ src, output = (test_path + s for s in ('.c', '.out'))
+ self.do_run_from_file(src, output)
+
def test_i32_mul_precise(self):
if self.emcc_args == None: return self.skip('needs ta2')
@@ -966,6 +971,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
self.do_run_from_file(src, output)
def test_strings(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('musl libc needs ta2')
test_path = path_from_root('tests', 'core', 'test_strings')
src, output = (test_path + s for s in ('.in', '.out'))
@@ -1160,7 +1166,6 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
self.do_run_from_file(src, output)
def test_longjmp_repeat(self):
- if os.environ.get('EMCC_FAST_COMPILER') == '0': Settings.MAX_SETJMPS = 1 # todo: do this more strict thing in fastcomp too
test_path = path_from_root('tests', 'core', 'test_longjmp_repeat')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
@@ -1184,8 +1189,6 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
self.do_run_from_file(src, output)
def test_setjmp_many(self):
- if os.environ.get('EMCC_FAST_COMPILER') != '0': return self.skip('todo in fastcomp: make MAX_SETJMPS take effect')
-
src = r'''
#include <stdio.h>
#include <setjmp.h>
@@ -1197,9 +1200,42 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
return 0;
}
'''
- for num in [Settings.MAX_SETJMPS, Settings.MAX_SETJMPS+1]:
- print num
- self.do_run(src.replace('NUM', str(num)), '0\n' * num if num <= Settings.MAX_SETJMPS or not Settings.ASM_JS else 'build with a higher value for MAX_SETJMPS')
+ for maxx in [Settings.MAX_SETJMPS/2, Settings.MAX_SETJMPS, 2*Settings.MAX_SETJMPS]:
+ Settings.MAX_SETJMPS = maxx
+ for num in [maxx, maxx+1]:
+ print maxx, num
+ self.do_run(src.replace('NUM', str(num)), '0\n' * num if num <= Settings.MAX_SETJMPS or not Settings.ASM_JS else 'build with a higher value for MAX_SETJMPS')
+
+ def test_setjmp_many_2(self):
+ if os.environ.get('EMCC_FAST_COMPILER') == '0': return self.skip('non-fastcomp do not hit the limit.')
+
+ src = r'''
+#include <setjmp.h>
+#include <stdio.h>
+
+jmp_buf env;
+
+void luaWork(int d){
+ int x;
+ printf("d is at %d\n", d);
+
+ longjmp(env, 1);
+}
+
+int main()
+{
+ const int ITERATIONS=25;
+ for(int i = 0; i < ITERATIONS; i++){
+ if(!setjmp(env)){
+ luaWork(i);
+ }
+ }
+ return 0;
+}
+'''
+
+ self.do_run(src, r'''d is at 19
+too many setjmps in a function call, build with a higher value for MAX_SETJMPS''')
def test_exceptions(self):
if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1")
@@ -1311,6 +1347,33 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co
test_path = path_from_root('tests', 'core', 'test_exceptions_white_list')
src, output = (test_path + s for s in ('.in', '.out'))
self.do_run_from_file(src, output)
+ size = len(open('src.cpp.o.js').read())
+ shutil.copyfile('src.cpp.o.js', 'orig.js')
+
+ if os.environ.get('EMCC_FAST_COMPILER') != '0':
+ # check that an empty whitelist works properly (as in, same as exceptions disabled)
+ empty_output = path_from_root('tests', 'core', 'test_exceptions_white_list_empty.out')
+
+ Settings.EXCEPTION_CATCHING_WHITELIST = []
+ self.do_run_from_file(src, empty_output)
+ empty_size = len(open('src.cpp.o.js').read())
+ shutil.copyfile('src.cpp.o.js', 'empty.js')
+
+ Settings.EXCEPTION_CATCHING_WHITELIST = ['fake']
+ self.do_run_from_file(src, empty_output)
+ fake_size = len(open('src.cpp.o.js').read())
+ shutil.copyfile('src.cpp.o.js', 'fake.js')
+
+ Settings.DISABLE_EXCEPTION_CATCHING = 1
+ self.do_run_from_file(src, empty_output)
+ disabled_size = len(open('src.cpp.o.js').read())
+ shutil.copyfile('src.cpp.o.js', 'disabled.js')
+
+ assert size - empty_size > 2000, [empty_size, size] # big change when we disable entirely
+ assert size - fake_size > 2000, [fake_size, size]
+ assert empty_size == fake_size, [empty_size, fake_size]
+ assert empty_size - disabled_size < 100, [empty_size, disabled_size] # full disable removes a tiny bit more
+ assert fake_size - disabled_size < 100, [disabled_size, fake_size]
def test_exceptions_white_list_2(self):
Settings.DISABLE_EXCEPTION_CATCHING = 2
@@ -2032,10 +2095,10 @@ def process(filename):
if self.emcc_args and '-O2' in self.emcc_args:
# Make sure ALLOW_MEMORY_GROWTH generates different code (should be less optimized)
- code_start = 'var TOTAL_MEMORY = '
+ code_start = 'var TOTAL_MEMORY'
fail = fail[fail.find(code_start):]
win = win[win.find(code_start):]
- assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller'
+ assert len(fail) < len(win), 'failing code - without memory growth on - is more optimized, and smaller' + str([len(fail), len(win)])
def test_ssr(self): # struct self-ref
src = '''
@@ -2461,6 +2524,7 @@ The current type of b is: 9
self.do_run_from_file(src, output)
def test_strtol_hex(self):
+ if self.run_name.startswith('s_'): return self.skip('Needs musl libc.')
# tests strtoll for hex strings (0x...)
test_path = path_from_root('tests', 'core', 'test_strtol_hex')
src, output = (test_path + s for s in ('.in', '.out'))
@@ -2468,6 +2532,7 @@ The current type of b is: 9
self.do_run_from_file(src, output)
def test_strtol_dec(self):
+ if self.run_name.startswith('s_'): return self.skip('Needs musl libc.')
# tests strtoll for decimal strings (0x...)
test_path = path_from_root('tests', 'core', 'test_strtol_dec')
src, output = (test_path + s for s in ('.in', '.out'))
@@ -2475,6 +2540,7 @@ The current type of b is: 9
self.do_run_from_file(src, output)
def test_strtol_bin(self):
+ if self.run_name.startswith('s_'): return self.skip('Needs musl libc.')
# tests strtoll for binary strings (0x...)
test_path = path_from_root('tests', 'core', 'test_strtol_bin')
src, output = (test_path + s for s in ('.in', '.out'))
@@ -2482,6 +2548,7 @@ The current type of b is: 9
self.do_run_from_file(src, output)
def test_strtol_oct(self):
+ if self.run_name.startswith('s_'): return self.skip('Needs musl libc.')
# tests strtoll for decimal strings (0x...)
test_path = path_from_root('tests', 'core', 'test_strtol_oct')
src, output = (test_path + s for s in ('.in', '.out'))
@@ -3204,7 +3271,7 @@ def process(filename):
break
else:
raise Exception('Could not find symbol table!')
- table = table[table.find('{'):table.rfind('}')+1]
+ table = table[table.find('{'):table.find('}')+1]
# ensure there aren't too many globals; we don't want unnamed_addr
assert table.count(',') <= 4
@@ -3713,6 +3780,7 @@ int main()
self.do_run(src, expected)
def test_transtrcase(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('musl libc needs ta2')
test_path = path_from_root('tests', 'core', 'test_transtrcase')
src, output = (test_path + s for s in ('.in', '.out'))
@@ -4608,6 +4676,7 @@ int main(void) {
### 'Medium' tests
def test_fannkuch(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('musl libc needs ta2')
try:
if self.run_name == 'slow2' or self.run_name == 'slow2asm':
old_target = os.environ.get('EMCC_LLVM_TARGET') or ''
@@ -4652,8 +4721,7 @@ int main(void) {
if self.emcc_args is None: self.emcc_args = [] # dlmalloc auto-inclusion is only done if we use emcc
self.banned_js_engines = [NODE_JS] # slower, and fail on 64-bit
- Settings.CORRECT_SIGNS = 2
- Settings.CORRECT_SIGNS_LINES = ['src.cpp:' + str(i+4) for i in [4816, 4191, 4246, 4199, 4205, 4235, 4227]]
+ Settings.CORRECT_SIGNS = 1
Settings.TOTAL_MEMORY = 128*1024*1024 # needed with typed arrays
src = open(path_from_root('system', 'lib', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read()
@@ -5412,9 +5480,6 @@ def process(filename):
### Integration tests
def test_ccall(self):
- if self.emcc_args is not None and '-O2' in self.emcc_args:
- self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right
-
post = '''
def process(filename):
src = \'\'\'
@@ -5458,6 +5523,11 @@ def process(filename):
self.do_run_from_file(src, output, post_build=post)
+ if self.emcc_args is not None and '-O2' in self.emcc_args:
+ print 'with closure'
+ self.emcc_args += ['--closure', '1']
+ self.do_run_from_file(src, output, post_build=post)
+
def test_pgo(self):
if Settings.ASM_JS: return self.skip('PGO does not work in asm mode')
diff --git a/tests/test_other.py b/tests/test_other.py
index 349a16b4..03859a4e 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -203,6 +203,10 @@ Options that are modified or new in %s include:
(['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'),
(['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'),
(['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'),
+ ([], lambda generated: '// The Module object' in generated, 'without opts, comments in shell code'),
+ (['-O2'], lambda generated: '// The Module object' not in generated, 'with opts, no comments in shell code'),
+ (['-O2', '-g2'], lambda generated: '// The Module object' not in generated, 'with -g2, no comments in shell code'),
+ (['-O2', '-g3'], lambda generated: '// The Module object' in generated, 'with -g3, yes comments in shell code'),
]:
print params, text
self.clear()
@@ -1027,10 +1031,93 @@ This pointer might make sense in another type signature: i: 0
Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b
# a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though
- Building.emcc(main_name, ['-L.', '-lLIB', a_name+'.o', c_name + '.o'], output_filename='a.out.js')
+ Building.emcc(main_name, [a_name+'.o', c_name + '.o', '-L.', '-lLIB'], output_filename='a.out.js')
self.assertContained('result: 62', run_js(os.path.join(self.get_dir(), 'a.out.js')))
+ def test_link_group_asserts(self):
+ lib_src_name = os.path.join(self.get_dir(), 'lib.c')
+ open(lib_src_name, 'w').write('int x() { return 42; }')
+
+ main_name = os.path.join(self.get_dir(), 'main.c')
+ open(main_name, 'w').write(r'''
+ #include <stdio.h>
+ int x();
+ int main() {
+ printf("result: %d\n", x());
+ return 0;
+ }
+ ''')
+
+ Building.emcc(lib_src_name) # lib.c.o
+ lib_name = os.path.join(self.get_dir(), 'libLIB.a')
+ Building.emar('cr', lib_name, [lib_src_name + '.o']) # libLIB.a with lib.c.o
+
+ def test(lib_args, err_expected):
+ output = Popen([PYTHON, EMCC, main_name, '-o', 'a.out.js'] + lib_args, stdout=PIPE, stderr=PIPE).communicate()
+ if err_expected:
+ self.assertContained(err_expected, output[1])
+ else:
+ out_js = os.path.join(self.get_dir(), 'a.out.js')
+ assert os.path.exists(out_js), '\n'.join(output)
+ self.assertContained('result: 42', run_js(out_js))
+
+ test(['-Wl,--start-group', lib_name], '--start-group without matching --end-group')
+ test(['-Wl,--start-group', lib_name, '-Wl,--start-group'], 'Nested --start-group, missing --end-group?')
+ test(['-Wl,--end-group', lib_name, '-Wl,--start-group'], '--end-group without --start-group')
+ test(['-Wl,--start-group', lib_name, '-Wl,--end-group'], None)
+
+ def test_circular_libs(self):
+ def tmp_source(name, code):
+ file_name = os.path.join(self.get_dir(), name)
+ open(file_name, 'w').write(code)
+ return file_name
+
+ a = tmp_source('a.c', 'int z(); int x() { return z(); }')
+ b = tmp_source('b.c', 'int x(); int y() { return x(); } int z() { return 42; }')
+ c = tmp_source('c.c', 'int q() { return 0; }')
+ main = tmp_source('main.c', r'''
+ #include <stdio.h>
+ int y();
+ int main() {
+ printf("result: %d\n", y());
+ return 0;
+ }
+ ''')
+
+ Building.emcc(a) # a.c.o
+ Building.emcc(b) # b.c.o
+ Building.emcc(c) # c.c.o
+ lib_a = os.path.join(self.get_dir(), 'libA.a')
+ Building.emar('cr', lib_a, [a + '.o', c + '.o']) # libA.a with a.c.o,c.c.o
+ lib_b = os.path.join(self.get_dir(), 'libB.a')
+ Building.emar('cr', lib_b, [b + '.o', c + '.o']) # libB.a with b.c.o,c.c.o
+
+ args = ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1', main, '-o', 'a.out.js']
+ libs_list = [lib_a, lib_b]
+
+ # lib_a does not satisfy any symbols from main, so it will not be included,
+ # and there will be an unresolved symbol.
+ output = Popen([PYTHON, EMCC] + args + libs_list, stdout=PIPE, stderr=PIPE).communicate()
+ self.assertContained('error: unresolved symbol: x', output[1])
+
+ # -Wl,--start-group and -Wl,--end-group around the libs will cause a rescan
+ # of lib_a after lib_b adds undefined symbol "x", so a.c.o will now be
+ # included (and the link will succeed).
+ libs = ['-Wl,--start-group'] + libs_list + ['-Wl,--end-group']
+ output = Popen([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE).communicate()
+ out_js = os.path.join(self.get_dir(), 'a.out.js')
+ assert os.path.exists(out_js), '\n'.join(output)
+ self.assertContained('result: 42', run_js(out_js))
+
+ # -( and -) should also work.
+ args = ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1', main, '-o', 'a2.out.js']
+ libs = ['-Wl,-('] + libs_list + ['-Wl,-)']
+ output = Popen([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE).communicate()
+ out_js = os.path.join(self.get_dir(), 'a2.out.js')
+ assert os.path.exists(out_js), '\n'.join(output)
+ self.assertContained('result: 42', run_js(out_js))
+
def test_redundant_link(self):
lib = "int mult() { return 1; }"
lib_name = os.path.join(self.get_dir(), 'libA.c')
@@ -2135,7 +2222,7 @@ void wakaw::Cm::RasterBase<wakaw::watwat::Polocator?>(unsigned int*, unsigned in
test_js_closure_0 = open(path_from_root('tests', 'Module-exports', 'test.js')).read()
# Check that test.js compiled with --closure 0 contains "module['exports'] = Module;"
- assert "module['exports'] = Module;" in test_js_closure_0
+ assert ("module['exports'] = Module;" in test_js_closure_0) or ('module["exports"]=Module' in test_js_closure_0)
# Check that main.js (which requires test.js) completes successfully when run in node.js
# in order to check that the exports are indeed functioning correctly.
@@ -2791,3 +2878,14 @@ int main(int argc, char **argv) {
assert sizes[0] == 7 # no aliasing, all unique, fat tables
assert sizes[1] == 3 # aliased once more
+ def test_bad_export(self):
+ for m in ['', ' ']:
+ self.clear()
+ cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'EXPORTED_FUNCTIONS=["' + m + '_main"]']
+ print cmd
+ stdout, stderr = Popen(cmd, stderr=PIPE).communicate()
+ if m:
+ assert 'function requested to be exported, but not implemented: " _main"' in stderr, stderr
+ else:
+ self.assertContained('hello, world!', run_js('a.out.js'))
+
diff --git a/tests/test_sanity.py b/tests/test_sanity.py
index 3d3da523..3ebd49b6 100644
--- a/tests/test_sanity.py
+++ b/tests/test_sanity.py
@@ -240,7 +240,7 @@ class sanity(RunnerCore):
os.chmod(path_from_root('tests', 'fake', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
os.chmod(path_from_root('tests', 'fake', 'bin', 'clang++'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
try_delete(SANITY_FILE)
- output = self.check_working(EMCC, 'did not see a source tree above LLVM_DIR, could not verify version numbers match')
+ output = self.check_working(EMCC, 'did not see a source tree above the LLVM root directory')
VERSION_WARNING = 'Emscripten, llvm and clang versions do not match, this is dangerous'
diff --git a/tests/webidl/output.txt b/tests/webidl/output.txt
index b874d928..c029935f 100644
--- a/tests/webidl/output.txt
+++ b/tests/webidl/output.txt
@@ -1,6 +1,7 @@
Parent:42
*
84
+object
c1
Parent:7
Child1:7
@@ -10,6 +11,7 @@ Child1:7
588
14
28
+Child1::parentFunc(90)
c1 v2
Parent:16
Child1:15
diff --git a/tests/webidl/post.js b/tests/webidl/post.js
index 444efcd1..5376f27b 100644
--- a/tests/webidl/post.js
+++ b/tests/webidl/post.js
@@ -5,6 +5,8 @@ var sme = new Module.Parent(42);
sme.mulVal(2);
Module.print('*')
Module.print(sme.getVal());
+sme.parentFunc(90);
+Module.print(typeof sme.getAsConst());
Module.print('c1');
@@ -16,6 +18,7 @@ Module.print(c1.getValSqr());
Module.print(c1.getValSqr(3));
Module.print(c1.getValTimes()); // default argument should be 1
Module.print(c1.getValTimes(2));
+c1.parentFunc(90);
Module.print('c1 v2');
diff --git a/tests/webidl/test.h b/tests/webidl/test.h
index 903f8f78..5d13846c 100644
--- a/tests/webidl/test.h
+++ b/tests/webidl/test.h
@@ -10,6 +10,8 @@ public:
Parent(Parent *p, Parent *q); // overload constructor
int getVal() { return value; }; // inline should work just fine here, unlike Way 1 before
void mulVal(int mul);
+ void parentFunc() {}
+ const Parent *getAsConst() { return NULL; }
};
class Child1 : public Parent {
@@ -19,6 +21,7 @@ public:
int getValSqr() { return value*value; }
int getValSqr(int more) { return value*value*more; }
int getValTimes(int times=1) { return value*times; }
+ void parentFunc(int x) { printf("Child1::parentFunc(%d)\n", x); }
};
// Child2 has vtable, parent does not. Checks we cast child->parent properly - (Parent*)child is not a no-op, must offset
diff --git a/tests/webidl/test.idl b/tests/webidl/test.idl
index 98ab5070..8ee82b76 100644
--- a/tests/webidl/test.idl
+++ b/tests/webidl/test.idl
@@ -5,12 +5,15 @@ interface Parent {
void Parent(long val);
long getVal();
void mulVal(long mul);
+ void parentFunc();
+ [Const] Parent getAsConst();
};
interface Child1 {
void Child1(optional long val);
long getValSqr(optional long more);
long getValTimes(optional long times=1);
+ void parentFunc(long x); // redefinition, name collides with parent
};
Child1 implements Parent;