Beispiel #1
0
void CStdDeserializer::ScriptString(const char* name, JS::MutableHandleString out)
{
#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: probably need to convert JS strings from little-endian
#endif

	JSContext* cx = m_ScriptInterface.GetContext();
	JSAutoRequest rq(cx);

	bool isLatin1;
	Bool("isLatin1", isLatin1);
	if (isLatin1)
	{
		std::vector<JS::Latin1Char> str;
		ReadStringLatin1(name, str);

		out.set(JS_NewStringCopyN(cx, (const char*)str.data(), str.size()));
		if (!out)
			throw PSERROR_Deserialize_ScriptError("JS_NewStringCopyN failed");
	}
	else
	{
		utf16string str;
		ReadStringUTF16(name, str);

		out.set(JS_NewUCStringCopyN(cx, (const char16_t*)str.data(), str.length()));
		if (!out)
			throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed");
	}
}
Beispiel #2
0
void CStdDeserializer::ScriptString(const char* UNUSED(name), JSString*& out)
{
	utf16string str;
	ReadStringUTF16(str);

#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: probably need to convert JS strings from little-endian
#endif

	out = JS_NewUCStringCopyN(m_ScriptInterface.GetContext(), (const jschar*)str.data(), str.length());
	if (!out)
		throw PSERROR_Deserialize_ScriptError("JS_NewUCStringCopyN failed");
}
Beispiel #3
0
jsval CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleObject appendParent)
{
	JSContext* cx = m_ScriptInterface.GetContext();
	JSAutoRequest rq(cx);

	uint8_t type;
	NumberU8_Unbounded("type", type);
	switch (type)
	{
	case SCRIPT_TYPE_VOID:
		return JS::UndefinedValue();

	case SCRIPT_TYPE_NULL:
		return JS::NullValue();

	case SCRIPT_TYPE_ARRAY:
	case SCRIPT_TYPE_OBJECT:
	case SCRIPT_TYPE_OBJECT_PROTOTYPE:
	{
		JS::RootedObject obj(cx);
		if (appendParent)
		{
			obj.set(appendParent);
		}
		else if (type == SCRIPT_TYPE_ARRAY)
		{
			u32 length;
			NumberU32_Unbounded("array length", length);
			obj.set(JS_NewArrayObject(cx, length));
		}
		else if (type == SCRIPT_TYPE_OBJECT)
		{
			obj.set(JS_NewPlainObject(cx));
		}
		else // SCRIPT_TYPE_OBJECT_PROTOTYPE
		{
			std::wstring prototypeName;
			String("proto name", prototypeName, 0, 256);

			// Get constructor object
			JS::RootedObject proto(cx);
			GetSerializablePrototype(prototypeName, &proto);
			if (!proto)
				throw PSERROR_Deserialize_ScriptError("Failed to find serializable prototype for object");

			JS::RootedObject parent(cx, JS_GetParent(proto));
			if (!proto || !parent)
				throw PSERROR_Deserialize_ScriptError();

			// TODO: Remove support for parent since this is dropped upstream SpiderMonkey
			obj.set(JS_NewObjectWithGivenProto(cx, nullptr, proto, parent));
			if (!obj)
				throw PSERROR_Deserialize_ScriptError("JS_NewObject failed");

			// Does it have custom Deserialize function?
			// if so, we let it handle the deserialized data, rather than adding properties directly
			bool hasCustomDeserialize, hasCustomSerialize;
			if (!JS_HasProperty(cx, obj, "Serialize", &hasCustomSerialize) || !JS_HasProperty(cx, obj, "Deserialize", &hasCustomDeserialize))
				throw PSERROR_Serialize_ScriptError("JS_HasProperty failed");

			if (hasCustomDeserialize)
			{
				AddScriptBackref(obj);

				JS::RootedValue serialize(cx);
				if (!JS_GetProperty(cx, obj, "Serialize", &serialize))
					throw PSERROR_Serialize_ScriptError("JS_GetProperty failed");
				bool hasNullSerialize = hasCustomSerialize && serialize.isNull();

				// If Serialize is null, we'll still call Deserialize but with undefined argument
				JS::RootedValue data(cx);
				if (!hasNullSerialize)
					ScriptVal("data", &data);

				JS::RootedValue objVal(cx, JS::ObjectValue(*obj));
				m_ScriptInterface.CallFunctionVoid(objVal, "Deserialize", data);

				return JS::ObjectValue(*obj);
			}
		}

		if (!obj)
			throw PSERROR_Deserialize_ScriptError("Deserializer failed to create new object");

		AddScriptBackref(obj);

		uint32_t numProps;
		NumberU32_Unbounded("num props", numProps);
		bool isLatin1;
		for (uint32_t i = 0; i < numProps; ++i)
		{
			Bool("isLatin1", isLatin1);
			if (isLatin1)
			{
				std::vector<JS::Latin1Char> propname;
				ReadStringLatin1("prop name", propname);
				JS::RootedValue propval(cx, ReadScriptVal("prop value", JS::NullPtr()));

				utf16string prp(propname.begin(), propname.end());;
// TODO: Should ask upstream about getting a variant of JS_SetProperty with a length param.
				if (!JS_SetUCProperty(cx, obj, (const char16_t*)prp.data(), prp.length(), propval))
					throw PSERROR_Deserialize_ScriptError();
			}
			else
			{
				utf16string propname;
				ReadStringUTF16("prop name", propname);
				JS::RootedValue propval(cx, ReadScriptVal("prop value", JS::NullPtr()));

				if (!JS_SetUCProperty(cx, obj, (const char16_t*)propname.data(), propname.length(), propval))
					throw PSERROR_Deserialize_ScriptError();
			}
		}

		return JS::ObjectValue(*obj);
	}
	case SCRIPT_TYPE_STRING:
	{
		JS::RootedString str(cx);
		ScriptString("string", &str);
		return JS::StringValue(str);
	}
	case SCRIPT_TYPE_INT:
	{
		int32_t value;
		NumberI32("value", value, JSVAL_INT_MIN, JSVAL_INT_MAX);
		return JS::NumberValue(value);
	}
	case SCRIPT_TYPE_DOUBLE:
	{
		double value;
		NumberDouble_Unbounded("value", value);
		JS::RootedValue rval(cx, JS::NumberValue(value));
		if (rval.isNull())
			throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed");
		return rval;
	}
	case SCRIPT_TYPE_BOOLEAN:
	{
		uint8_t value;
		NumberU8("value", value, 0, 1);
		return JS::BooleanValue(value ? true : false);
	}
	case SCRIPT_TYPE_BACKREF:
	{
		u32 tag;
		NumberU32_Unbounded("tag", tag);
		JS::RootedObject obj(cx);
		GetScriptBackref(tag, &obj);
		if (!obj)
			throw PSERROR_Deserialize_ScriptError("Invalid backref tag");
		return JS::ObjectValue(*obj);
	}
	case SCRIPT_TYPE_OBJECT_NUMBER:
	{
		double value;
		NumberDouble_Unbounded("value", value);
		JS::RootedValue val(cx, JS::NumberValue(value));

		JS::RootedObject ctorobj(cx);
		if (!JS_GetClassObject(cx, JSProto_Number, &ctorobj))
			throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");

		JS::RootedObject obj(cx, JS_New(cx, ctorobj, JS::HandleValueArray(val)));
		if (!obj)
			throw PSERROR_Deserialize_ScriptError("JS_New failed");
		AddScriptBackref(obj);
		return JS::ObjectValue(*obj);
	}
	case SCRIPT_TYPE_OBJECT_STRING:
	{
		JS::RootedString str(cx);
		ScriptString("value", &str);
		if (!str)
			throw PSERROR_Deserialize_ScriptError();
		JS::RootedValue val(cx, JS::StringValue(str));

		JS::RootedObject ctorobj(cx);
		if (!JS_GetClassObject(cx, JSProto_String, &ctorobj))
			throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");

		JS::RootedObject obj(cx, JS_New(cx, ctorobj, JS::HandleValueArray(val)));
		if (!obj)
			throw PSERROR_Deserialize_ScriptError("JS_New failed");
		AddScriptBackref(obj);
		return JS::ObjectValue(*obj);
	}
	case SCRIPT_TYPE_OBJECT_BOOLEAN:
	{
		bool value;
		Bool("value", value);
		JS::RootedValue val(cx, JS::BooleanValue(value));

		JS::RootedObject ctorobj(cx);
		if (!JS_GetClassObject(cx, JSProto_Boolean, &ctorobj))
			throw PSERROR_Deserialize_ScriptError("JS_GetClassObject failed");

		JS::RootedObject obj(cx, JS_New(cx, ctorobj, JS::HandleValueArray(val)));
		if (!obj)
			throw PSERROR_Deserialize_ScriptError("JS_New failed");
		AddScriptBackref(obj);
		return JS::ObjectValue(*obj);
	}
	case SCRIPT_TYPE_TYPED_ARRAY:
	{
		u8 arrayType;
		u32 byteOffset, length;
		NumberU8_Unbounded("array type", arrayType);
		NumberU32_Unbounded("byte offset", byteOffset);
		NumberU32_Unbounded("length", length);

		// To match the serializer order, we reserve the typed array's backref tag here
		JS::RootedObject arrayObj(cx);
		AddScriptBackref(arrayObj);

		// Get buffer object
		JS::RootedValue bufferVal(cx, ReadScriptVal("buffer", JS::NullPtr()));

		if (!bufferVal.isObject())
			throw PSERROR_Deserialize_ScriptError();

		JS::RootedObject bufferObj(cx, &bufferVal.toObject());
		if (!JS_IsArrayBufferObject(bufferObj))
			throw PSERROR_Deserialize_ScriptError("js_IsArrayBuffer failed");

		switch(arrayType)
		{
		case SCRIPT_TYPED_ARRAY_INT8:
			arrayObj = JS_NewInt8ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_UINT8:
			arrayObj = JS_NewUint8ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_INT16:
			arrayObj = JS_NewInt16ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_UINT16:
			arrayObj = JS_NewUint16ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_INT32:
			arrayObj = JS_NewInt32ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_UINT32:
			arrayObj = JS_NewUint32ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_FLOAT32:
			arrayObj = JS_NewFloat32ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_FLOAT64:
			arrayObj = JS_NewFloat64ArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		case SCRIPT_TYPED_ARRAY_UINT8_CLAMPED:
			arrayObj = JS_NewUint8ClampedArrayWithBuffer(cx, bufferObj, byteOffset, length);
			break;
		default:
			throw PSERROR_Deserialize_ScriptError("Failed to deserialize unrecognized typed array view");
		}
		if (!arrayObj)
			throw PSERROR_Deserialize_ScriptError("js_CreateTypedArrayWithBuffer failed");

		return JS::ObjectValue(*arrayObj);
	}
	case SCRIPT_TYPE_ARRAY_BUFFER:
	{
		u32 length;
		NumberU32_Unbounded("buffer length", length);

#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: need to convert JS ArrayBuffer data from little-endian
#endif
		void* contents = malloc(length);
		ENSURE(contents);
		RawBytes("buffer data", (u8*)contents, length);
		JS::RootedObject bufferObj(cx, JS_NewArrayBufferWithContents(cx, length, contents));
		AddScriptBackref(bufferObj);

		return JS::ObjectValue(*bufferObj);
	}
	case SCRIPT_TYPE_OBJECT_MAP:
	{
		JS::RootedObject obj(cx, JS::NewMapObject(cx));
		AddScriptBackref(obj);

		u32 mapSize;
		NumberU32_Unbounded("map size", mapSize);

		for (u32 i=0; i<mapSize; ++i)
		{
			JS::RootedValue key(cx, ReadScriptVal("map key", JS::NullPtr()));
			JS::RootedValue value(cx, ReadScriptVal("map value", JS::NullPtr()));
			JS::MapSet(cx, obj, key, value);
		}

		return JS::ObjectValue(*obj);
	}
	case SCRIPT_TYPE_OBJECT_SET:
	{
		JS::RootedValue setVal(cx);
		m_ScriptInterface.Eval("(new Set())", &setVal);

		JS::RootedObject setObj(cx, &setVal.toObject());
		AddScriptBackref(setObj);

		u32 setSize;
		NumberU32_Unbounded("set size", setSize);

		for (u32 i=0; i<setSize; ++i)
		{
			JS::RootedValue value(cx, ReadScriptVal("set value", JS::NullPtr()));
			m_ScriptInterface.CallFunctionVoid(setVal, "add", value);
		}

		return setVal;
	}
	default:
		throw PSERROR_Deserialize_OutOfBounds();
	}
}
Beispiel #4
0
jsval CStdDeserializer::ReadScriptVal(JSObject* appendParent)
{
	JSContext* cx = m_ScriptInterface.GetContext();

	uint8_t type;
	NumberU8_Unbounded("type", type);
	switch (type)
	{
	case SCRIPT_TYPE_VOID:
		return JSVAL_VOID;

	case SCRIPT_TYPE_NULL:
		return JSVAL_NULL;

	case SCRIPT_TYPE_ARRAY:
	case SCRIPT_TYPE_OBJECT:
	{
		JSObject* obj;
		if (appendParent)
			obj = appendParent;
		else if (type == SCRIPT_TYPE_ARRAY)
			obj = JS_NewArrayObject(cx, 0, NULL);
		else
			obj = JS_NewObject(cx, NULL, NULL, NULL);

		if (!obj)
			throw PSERROR_Deserialize_ScriptError();
		CScriptValRooted objRoot(cx, OBJECT_TO_JSVAL(obj));

		AddScriptBackref(obj);

		uint32_t numProps;
		NumberU32_Unbounded("num props", numProps);

		for (uint32_t i = 0; i < numProps; ++i)
		{
			utf16string propname;
			ReadStringUTF16(propname);

			jsval propval = ReadScriptVal(NULL);
			CScriptValRooted propvalRoot(cx, propval);

			if (!JS_SetUCProperty(cx, obj, (const jschar*)propname.data(), propname.length(), &propval))
				throw PSERROR_Deserialize_ScriptError();
		}

		return OBJECT_TO_JSVAL(obj);
	}
	case SCRIPT_TYPE_STRING:
	{
		JSString* str;
		ScriptString("string", str);
		return STRING_TO_JSVAL(str);
	}
	case SCRIPT_TYPE_INT:
	{
		int32_t value;
		NumberI32("value", value, JSVAL_INT_MIN, JSVAL_INT_MAX);
		return INT_TO_JSVAL(value);
	}
	case SCRIPT_TYPE_DOUBLE:
	{
		double value;
		NumberDouble_Unbounded("value", value);
		jsval rval;
		if (!JS_NewNumberValue(cx, value, &rval))
			throw PSERROR_Deserialize_ScriptError("JS_NewNumberValue failed");
		return rval;
	}
	case SCRIPT_TYPE_BOOLEAN:
	{
		uint8_t value;
		NumberU8("value", value, 0, 1);
		return BOOLEAN_TO_JSVAL(value ? JS_TRUE : JS_FALSE);
	}
	case SCRIPT_TYPE_BACKREF:
	{
		u32 tag;
		NumberU32_Unbounded("tag", tag);
		JSObject* obj = GetScriptBackref(tag);
		if (!obj)
			throw PSERROR_Deserialize_ScriptError("Invalid backref tag");
		return OBJECT_TO_JSVAL(obj);
	}
	default:
		throw PSERROR_Deserialize_OutOfBounds();
	}
}