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..ace5e854a9 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,16 @@ Vector2D.prototype.add = function(v)
 	return this;
 };
 
+Vector2D.prototype.lengthSquared = function()
+{
+	return this.x*this.x + this.y*this.y
+};
+
+Vector2D.prototype.nop = function()
+{
+	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..87e458da81 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,17 @@ 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)}];
+};
+
+TestScript1_vector.prototype.GetX = function() {
+	return this.list[0].pos.lengthSquared();
+};
+
+Engine.RegisterComponentType(IID_Test1, "TestScript1_vector", TestScript1_vector);
diff --git a/source/network/NetMessageSim.cpp b/source/network/NetMessageSim.cpp
index 28b0dfeb33..6efda7f535 100644
--- a/source/network/NetMessageSim.cpp
+++ b/source/network/NetMessageSim.cpp
@@ -141,7 +141,7 @@ u8* CSimulationMessage::Serialize(u8* pBuffer) const
 	serializer.NumberI32_Unbounded("player", m_Player);
 	serializer.NumberU32_Unbounded("turn", m_Turn);
 
-	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
+	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data), false);
 	return serializer.GetBuffer();
 }
 
@@ -170,7 +170,7 @@ size_t CSimulationMessage::GetSerializedLength() const
 
 	// TODO: The cast can probably be removed if and when ScriptVal can take a JS::HandleValue instead of
 	// a JS::MutableHandleValue (relies on JSAPI change). Also search for other casts like this one in that case.
-	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
+	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data), false);
 	return CNetMessage::GetSerializedLength() + serializer.GetLength();
 }
 
@@ -200,7 +200,7 @@ u8* CGameSetupMessage::Serialize(u8* pBuffer) const
 	// TODO: ought to handle serialization exceptions
 	u8* pos = CNetMessage::Serialize(pBuffer);
 	CBufferBinarySerializer serializer(m_ScriptInterface, pos);
-	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
+	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data), false);
 	return serializer.GetBuffer();
 }
 
@@ -217,7 +217,7 @@ const u8* CGameSetupMessage::Deserialize(const u8* pStart, const u8* pEnd)
 size_t CGameSetupMessage::GetSerializedLength() const
 {
 	CLengthBinarySerializer serializer(m_ScriptInterface);
-	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
+	serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data), false);
 	return CNetMessage::GetSerializedLength() + serializer.GetLength();
 }
 
diff --git a/source/simulation2/components/CCmpAIManager.cpp b/source/simulation2/components/CCmpAIManager.cpp
index d62ee29205..ddf22bf9b0 100644
--- a/source/simulation2/components/CCmpAIManager.cpp
+++ b/source/simulation2/components/CCmpAIManager.cpp
@@ -671,7 +671,7 @@ public:
 			JS::RootedValue sharedData(cx);
 			if (!m_ScriptInterface->CallFunction(m_SharedAIObj, "Serialize", &sharedData))
 				LOGERROR("AI shared script Serialize call failed");
-			serializer.ScriptVal("sharedData", &sharedData);
+			serializer.ScriptVal("sharedData", &sharedData, false);
 		}
 		for (size_t i = 0; i < m_Players.size(); ++i)
 		{
@@ -684,7 +684,7 @@ public:
 			{
 				JS::RootedValue val(cx);
 				m_ScriptInterface->ReadStructuredClone(m_Players[i]->m_Commands[j], &val);
-				serializer.ScriptVal("command", &val);
+				serializer.ScriptVal("command", &val, false);
 			}
 
 			bool hasCustomSerialize = m_ScriptInterface->HasProperty(m_Players[i]->m_Obj, "Serialize");
@@ -693,11 +693,11 @@ public:
 				JS::RootedValue scriptData(cx);
 				if (!m_ScriptInterface->CallFunction(m_Players[i]->m_Obj, "Serialize", &scriptData))
 					LOGERROR("AI script Serialize call failed");
-				serializer.ScriptVal("data", &scriptData);
+				serializer.ScriptVal("data", &scriptData, false);
 			}
 			else
 			{
-				serializer.ScriptVal("data", &m_Players[i]->m_Obj);
+				serializer.ScriptVal("data", &m_Players[i]->m_Obj, false);
 			}
 		}
 
diff --git a/source/simulation2/components/CCmpCommandQueue.cpp b/source/simulation2/components/CCmpCommandQueue.cpp
index 90d4d3e5eb..fdf8ee4a2a 100644
--- a/source/simulation2/components/CCmpCommandQueue.cpp
+++ b/source/simulation2/components/CCmpCommandQueue.cpp
@@ -58,7 +58,7 @@ public:
 		for (size_t i = 0; i < m_LocalQueue.size(); ++i)
 		{
 			serialize.NumberI32_Unbounded("player", m_LocalQueue[i].player);
-			serialize.ScriptVal("data", &m_LocalQueue[i].data);
+			serialize.ScriptVal("data", &m_LocalQueue[i].data, false);
 		}
 	}
 
diff --git a/source/simulation2/scripting/ScriptComponent.cpp b/source/simulation2/scripting/ScriptComponent.cpp
index 4e96c7bdf5..39580e9994 100644
--- a/source/simulation2/scripting/ScriptComponent.cpp
+++ b/source/simulation2/scripting/ScriptComponent.cpp
@@ -82,11 +82,14 @@ void CComponentTypeScript::Serialize(ISerializer& serialize)
 		JS::RootedValue val(cx);
 		if (!m_ScriptInterface.CallFunction(m_Instance, "Serialize", &val))
 			LOGERROR("Script Serialize call failed");
-		serialize.ScriptVal("object", &val);
+		serialize.ScriptVal("object", &val, false); // TODO: make this new parameter optional
+		// TODO: or (maybe even better) provide another serialize function for it
+		// then we just need to make both call the right binaryserializer one in the end
 	}
 	else
 	{
-		serialize.ScriptVal("object", &m_Instance);
+		// We want to ignore the prototype of the top level object for components.
+		serialize.ScriptVal("object", &m_Instance, true);
 	}
 }
 
diff --git a/source/simulation2/serialization/BinarySerializer.cpp b/source/simulation2/serialization/BinarySerializer.cpp
index 2f1e737132..5f959e687b 100644
--- a/source/simulation2/serialization/BinarySerializer.cpp
+++ b/source/simulation2/serialization/BinarySerializer.cpp
@@ -26,6 +26,8 @@
 #include "scriptinterface/ScriptExtraHeaders.h"
 #include "SerializedScriptTypes.h"
 
+#include "ps/Filesystem.h" // TODO part fo the gvfs hack
+
 static u8 GetArrayType(js::Scalar::Type arrayType)
 {
 	switch(arrayType)
@@ -60,9 +62,18 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface&
 {
 	m_ScriptBackrefs.init();
 	m_SerializablePrototypes->init();
+
+	// TODO fix/remove the set functions for these prototypes, and let them just add things
+	// TODO call into the scriptinterface to let that add it
+	// TODO do the same thing for the deserializer
+	if (g_VFS) // TODO hack for tests
+	{
+		m_SerializablePrototypes->add(m_ScriptInterface.GetContext(), &m_ScriptInterface.GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).toObject(), L"Vector2D");
+		m_SerializablePrototypes->add(m_ScriptInterface.GetContext(), &m_ScriptInterface.GetCachedValue(ScriptInterface::CACHE_VECTOR3DPROTO).toObject(), L"Vector3D");
+	}
 }
 
-void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
+void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val, bool ignoreCustomPrototype)
 {
 	JSContext* cx = m_ScriptInterface.GetContext();
 	JSAutoRequest rq(cx);
@@ -159,13 +170,19 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
 				if (!proto)
 					throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed");
 
-				if (m_SerializablePrototypes->empty() || !IsSerializablePrototype(proto))
+				if (IdentifyStandardPrototype(proto) == JSProto_Object || ignoreCustomPrototype)
 				{
+debug_printf("default proto\n");
+
 					// Standard Object prototype
 					m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT);
+				}
+				else if (/*IdentifyStandardPrototype(proto) == JSProto_Null && */!IsSerializablePrototype(proto))
+				{
+					// TODO we could print the current value
 
-					// TODO: maybe we should throw an error for unrecognized non-Object prototypes?
-					//	(requires fixing AI serialization first and excluding component scripts)
+					// TODO: Can we get some more data about what the prototype is? (apart from null that is)
+					throw PSERROR_Serialize_ScriptError("Unhandled custom prototype");
 				}
 				else
 				{
@@ -174,6 +191,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
 
 					const std::wstring prototypeName = GetPrototypeName(proto);
 					m_Serializer.String("proto name", prototypeName, 0, 256);
+debug_printf("other %ls\n", prototypeName.c_str());
 
 					// Does it have custom Serialize function?
 					// if so, we serialize the data it returns, rather than the object's properties directly
diff --git a/source/simulation2/serialization/BinarySerializer.h b/source/simulation2/serialization/BinarySerializer.h
index 235b69a278..11a18c3c06 100644
--- a/source/simulation2/serialization/BinarySerializer.h
+++ b/source/simulation2/serialization/BinarySerializer.h
@@ -86,7 +86,7 @@ public:
 	CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer);
 
 	void ScriptString(const char* name, JS::HandleString string);
-	void HandleScriptVal(JS::HandleValue val);
+	void HandleScriptVal(JS::HandleValue val, bool ignoreCustomObjectPrototype = true);
 	void SetSerializablePrototypes(shared_ptr<ObjectIdCache<std::wstring> > prototypes);
 private:
 	const ScriptInterface& m_ScriptInterface;
@@ -206,9 +206,9 @@ protected:
 		m_Impl.Put(name, (u8*)value.data(), value.length());
 	}
 
-	virtual void PutScriptVal(const char* UNUSED(name), JS::MutableHandleValue value)
+	virtual void PutScriptVal(const char* UNUSED(name), JS::MutableHandleValue value, bool isComponentObject)
 	{
-		m_ScriptImpl->HandleScriptVal(value);
+		m_ScriptImpl->HandleScriptVal(value, isComponentObject);
 	}
 
 	virtual void PutRaw(const char* name, const u8* data, size_t len)
diff --git a/source/simulation2/serialization/DebugSerializer.cpp b/source/simulation2/serialization/DebugSerializer.cpp
index 7f1f49f1ec..ffff9bc492 100644
--- a/source/simulation2/serialization/DebugSerializer.cpp
+++ b/source/simulation2/serialization/DebugSerializer.cpp
@@ -147,7 +147,7 @@ void CDebugSerializer::PutString(const char* name, const std::string& value)
 	m_Stream << INDENT << name << ": " << "\"" << escaped << "\"\n";
 }
 
-void CDebugSerializer::PutScriptVal(const char* name, JS::MutableHandleValue value)
+void CDebugSerializer::PutScriptVal(const char* name, JS::MutableHandleValue value, bool)
 {
 	std::string source = m_ScriptInterface.ToString(value, true);
 
diff --git a/source/simulation2/serialization/DebugSerializer.h b/source/simulation2/serialization/DebugSerializer.h
index 7ad4871540..e30e6ba20d 100644
--- a/source/simulation2/serialization/DebugSerializer.h
+++ b/source/simulation2/serialization/DebugSerializer.h
@@ -54,7 +54,7 @@ protected:
 	virtual void PutNumber(const char* name, fixed value);
 	virtual void PutBool(const char* name, bool value);
 	virtual void PutString(const char* name, const std::string& value);
-	virtual void PutScriptVal(const char* name, JS::MutableHandleValue value);
+	virtual void PutScriptVal(const char* name, JS::MutableHandleValue value, bool);
 	virtual void PutRaw(const char* name, const u8* data, size_t len);
 
 private:
diff --git a/source/simulation2/serialization/ISerializer.cpp b/source/simulation2/serialization/ISerializer.cpp
index 92b9124e88..10c379fca7 100644
--- a/source/simulation2/serialization/ISerializer.cpp
+++ b/source/simulation2/serialization/ISerializer.cpp
@@ -92,9 +92,9 @@ void ISerializer::String(const char* name, const std::wstring& value, uint32_t m
 	PutString(name, str);
 }
 
-void ISerializer::ScriptVal(const char* name, JS::MutableHandleValue value)
+void ISerializer::ScriptVal(const char* name, JS::MutableHandleValue value, bool a)
 {
-	PutScriptVal(name, value);
+	PutScriptVal(name, value, a);
 }
 
 void ISerializer::RawBytes(const char* name, const u8* data, size_t len)
diff --git a/source/simulation2/serialization/ISerializer.h b/source/simulation2/serialization/ISerializer.h
index 4ab47bb0b7..4ca1016bab 100644
--- a/source/simulation2/serialization/ISerializer.h
+++ b/source/simulation2/serialization/ISerializer.h
@@ -220,7 +220,7 @@ public:
 	 * The value must not contain any unserializable values (like functions).
 	 * NOTE: We have to use a mutable handle because JS_Stringify requires that for unknown reasons.
 	 */
-	void ScriptVal(const char* name, JS::MutableHandleValue value);
+	void ScriptVal(const char* name, JS::MutableHandleValue value, bool);
 
 	/**
 	 * Serialize a stream of bytes.
@@ -257,7 +257,7 @@ protected:
 	virtual void PutBool(const char* name, bool value) = 0;
 	virtual void PutString(const char* name, const std::string& value) = 0;
 	// We have to use a mutable handle because JS_Stringify requires that for unknown reasons.
-	virtual void PutScriptVal(const char* name, JS::MutableHandleValue value) = 0;
+	virtual void PutScriptVal(const char* name, JS::MutableHandleValue value, bool) = 0;
 	virtual void PutRaw(const char* name, const u8* data, size_t len) = 0;
 };
 
diff --git a/source/simulation2/serialization/StdDeserializer.cpp b/source/simulation2/serialization/StdDeserializer.cpp
index 131898ccd9..6a1097841c 100644
--- a/source/simulation2/serialization/StdDeserializer.cpp
+++ b/source/simulation2/serialization/StdDeserializer.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2016 Wildfire Games.
+/* Copyright (C) 2017 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -27,6 +27,8 @@
 
 #include "lib/byte_order.h"
 
+#include "ps/Filesystem.h" // gvfs hack
+
 CStdDeserializer::CStdDeserializer(ScriptInterface& scriptInterface, std::istream& stream) :
 	m_ScriptInterface(scriptInterface), m_Stream(stream),
 	m_dummyObject(scriptInterface.GetJSRuntime())
@@ -40,6 +42,14 @@ CStdDeserializer::CStdDeserializer(ScriptInterface& scriptInterface, std::istrea
 	// needs to be serialized and then tagged
 	m_dummyObject = JS_NewPlainObject(cx);
 	m_ScriptBackrefs.push_back(JS::Heap<JSObject*>(m_dummyObject));
+
+	if (g_VFS)
+	{
+		JS::Heap<JSObject*> v2proto(&m_ScriptInterface.GetCachedValue(ScriptInterface::CACHE_VECTOR2DPROTO).toObject());
+		m_SerializablePrototypes.insert(std::make_pair(L"Vector2D", v2proto));
+		JS::Heap<JSObject*> v3proto(&m_ScriptInterface.GetCachedValue(ScriptInterface::CACHE_VECTOR3DPROTO).toObject());
+		m_SerializablePrototypes.insert(std::make_pair(L"Vector2D", v3proto));
+	}
 }
 
 CStdDeserializer::~CStdDeserializer()
@@ -512,16 +522,16 @@ void CStdDeserializer::SetSerializablePrototypes(std::map<std::wstring, JS::Heap
 	m_SerializablePrototypes = prototypes;
 }
 
-bool CStdDeserializer::IsSerializablePrototype(const std::wstring& name)
+bool CStdDeserializer::IsSerializablePrototype(const std::wstring& name) const
 {
 	return m_SerializablePrototypes.find(name) != m_SerializablePrototypes.end();
 }
 
-void CStdDeserializer::GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret)
+void CStdDeserializer::GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret) const
 {
-	std::map<std::wstring, JS::Heap<JSObject*> >::iterator it = m_SerializablePrototypes.find(name);
+	std::map<std::wstring, JS::Heap<JSObject*> >::const_iterator it = m_SerializablePrototypes.find(name);
 	if (it != m_SerializablePrototypes.end())
 		ret.set(it->second);
 	else
-		ret.set(NULL);
+		ret.set(nullptr);
 }
diff --git a/source/simulation2/serialization/StdDeserializer.h b/source/simulation2/serialization/StdDeserializer.h
index 1d16e56993..d2c3f48655 100644
--- a/source/simulation2/serialization/StdDeserializer.h
+++ b/source/simulation2/serialization/StdDeserializer.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2016 Wildfire Games.
+/* Copyright (C) 2017 Wildfire Games.
  * This file is part of 0 A.D.
  *
  * 0 A.D. is free software: you can redistribute it and/or modify
@@ -63,8 +63,8 @@ private:
 
 	std::map<std::wstring, JS::Heap<JSObject*> > m_SerializablePrototypes;
 
-	bool IsSerializablePrototype(const std::wstring& name);
-	void GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret);
+	bool IsSerializablePrototype(const std::wstring& name) const;
+	void GetSerializablePrototype(const std::wstring& name, JS::MutableHandleObject ret) const;
 };
 
 #endif // INCLUDED_STDDESERIALIZER
diff --git a/source/simulation2/tests/test_ComponentManager.h b/source/simulation2/tests/test_ComponentManager.h
index 1cbffd1602..735ecbb53b 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(), 5);
 
 		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\": 1,\n\
+        \"y\": 2\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(), 5);
 	}
 
 	void test_script_serialization_errors()
diff --git a/source/simulation2/tests/test_Serializer.h b/source/simulation2/tests/test_Serializer.h
index d9aee50552..4d160fc1cb 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);
 
@@ -302,14 +306,21 @@ public:
 		{
 			std::stringstream dbgstream;
 			CDebugSerializer serialize(script, dbgstream);
-			serialize.ScriptVal("script", &obj);
+			serialize.ScriptVal("script", &obj, false);
 			TS_ASSERT_STR_EQUALS(dbgstream.str(), debug);
 		}
 
 		std::stringstream stream;
 		CStdSerializer serialize(script, stream);
 
-		serialize.ScriptVal("script", &obj);
+		serialize.ScriptVal("script", &obj, false);
+
+// TODO hack
+if (loadglobalscripts) {
+	std::string foo_;
+	TSM_ASSERT(msg, script.CallFunction(obj, "nop", foo_));
+	TS_ASSERT_STR_EQUALS(foo_, "working");
+}
 
 		if (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, "working");
+}
 	}
 
 	void test_script_basic()
@@ -717,6 +735,37 @@ 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 */
+			39,
+			"\x0B" // SCRIPT_TYPE_OBJECT_PROTOTYPE
+			"\x08\0\0\0" // len of prototype name
+			"Vector2D" // prototype name
+			"\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]");
@@ -766,7 +815,7 @@ public:
 		TestLogger logger;
 
 		TS_ASSERT(script.Eval("([1, 2, function () { }])", &obj));
-		TS_ASSERT_THROWS(serialize.ScriptVal("script", &obj), PSERROR_Serialize_InvalidScriptValue);
+		TS_ASSERT_THROWS(serialize.ScriptVal("script", &obj, false), PSERROR_Serialize_InvalidScriptValue);
 	}
 
 	void test_script_splice()
@@ -801,7 +850,7 @@ public:
 			std::stringstream stream;
 			CStdSerializer serialize(script, stream);
 
-			serialize.ScriptVal("script", &obj);
+			serialize.ScriptVal("script", &obj, false);
 
 			CStdDeserializer deserialize(script, stream);
 
@@ -882,5 +931,4 @@ public:
 		DeleteDirectory(DataDir()/"_testcache");
 		CXeromyces::Terminate();
 	}
-
 };
