diff --git a/binaries/data/mods/_test.sim/globalscripts/test-global-helper.js b/binaries/data/mods/_test.sim/globalscripts/test-global-helper.js
index 884d084927..87fa440110 100644
--- a/binaries/data/mods/_test.sim/globalscripts/test-global-helper.js
+++ b/binaries/data/mods/_test.sim/globalscripts/test-global-helper.js
@@ -21,6 +21,12 @@ Vector2D.prototype.add = function(v)
 	return this;
 };
 
+Vector2D.prototype.nop = function(v)
+{
+warn("foo bar baz");
+	return "working";
+};
+
 function Vector3D(x, y, z)
 {
 	if (arguments.length == 3)
diff --git a/binaries/data/mods/_test.sim/simulation/components/test-serialize.js b/binaries/data/mods/_test.sim/simulation/components/test-serialize.js
index 9893d6d794..79ef74475f 100644
--- a/binaries/data/mods/_test.sim/simulation/components/test-serialize.js
+++ b/binaries/data/mods/_test.sim/simulation/components/test-serialize.js
@@ -88,3 +88,24 @@ TestScript1_consts.prototype.GetX = function() {
 };
 
 Engine.RegisterComponentType(IID_Test1, "TestScript1_consts", TestScript1_consts);
+
+// -------- //
+
+function TestScript1_vector() {}
+
+TestScript1_vector.prototype.Init = function() {
+	this.list = [ {pos: new Vector2D(1, 2)}];
+warn("foo");
+warn(typeof this.list[0].pos);
+warn(""+ (this.list[0].pos instanceof Vector2D));
+};
+
+TestScript1_vector.prototype.GetX = function() {
+warn(typeof this.list[0].pos);
+warn("" + (this.list[0].pos instanceof Vector2D));
+	this.list[0].pos.add({x: 1, y: 1}); // How not to write a getter; mostly to make sure it really is that type
+
+	return this.list[0].pos.x;
+};
+
+Engine.RegisterComponentType(IID_Test1, "TestScript1_vector", TestScript1_vector);
diff --git a/source/simulation2/tests/test_ComponentManager.h b/source/simulation2/tests/test_ComponentManager.h
index 73fcfacf20..7000ba80fc 100644
--- a/source/simulation2/tests/test_ComponentManager.h
+++ b/source/simulation2/tests/test_ComponentManager.h
@@ -716,11 +716,12 @@ public:
 		man.LoadComponentTypes();
 		TS_ASSERT(man.LoadScript(L"simulation/components/test-serialize.js"));
 
-		entity_id_t ent1 = 1, ent2 = 2, ent3 = 3, ent4 = 4;
+		entity_id_t ent1 = 1, ent2 = 2, ent3 = 3, ent4 = 4, ent5 = 5;
 		CEntityHandle hnd1 = man.AllocateEntityHandle(ent1);
 		CEntityHandle hnd2 = man.AllocateEntityHandle(ent2);
 		CEntityHandle hnd3 = man.AllocateEntityHandle(ent3);
 		CEntityHandle hnd4 = man.AllocateEntityHandle(ent4);
+		CEntityHandle hnd5 = man.AllocateEntityHandle(ent5);
 		CParamNode noParam;
 
 		CParamNode testParam;
@@ -729,6 +730,7 @@ public:
 		man.AddComponent(hnd1, man.LookupCID("TestScript1_values"), testParam);
 		man.AddComponent(hnd2, man.LookupCID("TestScript1_entity"), testParam);
 
+// TODO is this actually accurate?
 		// TODO: Since the upgrade to SpiderMonkey v24 this test won't be able to correctly represent
 		// non-tree structures because sharp variables were removed (bug 566700).
 		// This also affects the debug serializer and it could make sense to implement correct serialization again.
@@ -736,12 +738,15 @@ public:
 
 		man.AddComponent(hnd4, man.LookupCID("TestScript1_custom"), testParam);
 
+		man.AddComponent(hnd5, man.LookupCID("TestScript1_vector"), testParam);
+
 		TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent1, IID_Test1))->GetX(), 1234);
 		{
 			TestLogger log; // swallow warnings about this.entity being read-only
 			TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent2, IID_Test1))->GetX(), (int)ent2);
 		}
 		TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent3, IID_Test1))->GetX(), 8);
+		TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent5, IID_Test1))->GetX(), 2);
 
 		std::stringstream debugStream;
 		TS_ASSERT(man.DumpDebugState(debugStream, true));
@@ -780,6 +785,19 @@ entities:\n\
     object: {\n\
   \"c\": 1\n\
 }\n\
+\n\
+- id: 5\n\
+  TestScript1_vector:\n\
+    object: {\n\
+  \"list\": [\n\
+    {\n\
+      \"pos\": {\n\
+        \"x\": 2,\n\
+        \"y\": 3\n\
+      }\n\
+    }\n\
+  ]\n\
+}\n\
 \n"
 		);
 
@@ -799,6 +817,7 @@ entities:\n\
 			TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man2.QueryInterface(ent2, IID_Test1))->GetX(), (int)ent2);
 		}
 		TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man2.QueryInterface(ent3, IID_Test1))->GetX(), 12);
+		TS_ASSERT_EQUALS(static_cast<ICmpTest1*> (man.QueryInterface(ent5, IID_Test1))->GetX(), 3);
 	}
 
 	void test_script_serialization_errors()
diff --git a/source/simulation2/tests/test_Serializer.h b/source/simulation2/tests/test_Serializer.h
index d9aee50552..304675286a 100644
--- a/source/simulation2/tests/test_Serializer.h
+++ b/source/simulation2/tests/test_Serializer.h
@@ -289,9 +289,13 @@ public:
 
 	// TODO: test exceptions more thoroughly
 
-	void helper_script_roundtrip(const char* msg, const char* input, const char* expected, size_t expstreamlen = 0, const char* expstream = NULL, const char* debug = NULL)
+	void helper_script_roundtrip(const char* msg, const char* input, const char* expected, size_t expstreamlen = 0, const char* expstream = NULL, const char* debug = NULL, bool loadglobalscripts = false)
 	{
 		ScriptInterface script("Test", "Test", g_ScriptRuntime);
+
+		if (loadglobalscripts)
+			script.LoadGlobalScripts();
+
 		JSContext* cx = script.GetContext();
 		JSAutoRequest rq(cx);
 
@@ -311,6 +315,13 @@ public:
 
 		serialize.ScriptVal("script", &obj);
 
+// TODO hack
+if (loadglobalscripts) {
+	std::string foo_;
+	TSM_ASSERT(msg, script.CallFunction(obj, "nop", foo_));
+	TS_ASSERT_STR_EQUALS(foo_, "foobar");
+}
+
 		if (expstream)
 		{
 			TSM_ASSERT_STREAM(msg, stream, expstreamlen, expstream);
@@ -327,6 +338,13 @@ public:
 		std::string source;
 		TSM_ASSERT(msg, script.CallFunction(newobj, "toSource", source));
 		TS_ASSERT_STR_EQUALS(source, expected);
+
+// TODO hack
+if (loadglobalscripts) {
+	std::string foo;
+	TSM_ASSERT(msg, script.CallFunction(newobj, "nop", foo));
+	TS_ASSERT_STR_EQUALS(foo, "foobar");
+}
 	}
 
 	void test_script_basic()
@@ -717,6 +735,35 @@ public:
 
 	// TODO: prototype objects
 
+	void test_script_vector()
+	{
+		g_VFS = CreateVfs(20 * MiB);
+		TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
+		TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
+
+		helper_script_roundtrip("Vector2D",
+			"var a = new Vector2D(1, 2); a.add({x:1, y:1}); warn(\"\"+(a instanceof Vector2D)); a",
+		/* expected: */
+			"({x:2, y:3})",
+		/* expected stream */
+			27,
+			"\x03" // SCRIPT_TYPE_OBJECT
+			"\x02\0\0\0" // num props
+				"\x01\x01\0\0\0" "x" // "x"
+				"\x05" // SCRIPT_TYPE_INT
+				"\x02\0\0\0" // 2
+
+				"\x01\x01\0\0\0" "y" // "y"
+				"\x05" // SCRIPT_TYPE_INT
+				"\x03\0\0\0" // 3
+			,
+			NULL,
+			true
+				
+		);
+		g_VFS.reset();
+	}
+
 	void test_script_nonfinite()
 	{
 		helper_script_roundtrip("nonfinite", "[0, Infinity, -Infinity, NaN, -1/Infinity]", "[0, Infinity, -Infinity, NaN, -0]");
@@ -882,5 +929,4 @@ public:
 		DeleteDirectory(DataDir()/"_testcache");
 		CXeromyces::Terminate();
 	}
-
 };
