From 88f8ec69ce89fb558cac34156bc6bde04f5a0dfe Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Thu, 25 Jul 2013 17:57:04 -0700 Subject: [PATCH] Bug 578700 - Binary Data: initialize architecture. r=nmatsakis --HG-- extra : amend_source : 7775dc457c0c25283608e583472dc7cf04bffe8c --- js/src/Makefile.in | 4 + js/src/builtin/BinaryData.cpp | 201 ++++++++++++++++++ js/src/builtin/BinaryData.h | 103 +++++++++ js/src/builtin/TestingFunctions.cpp | 8 + js/src/jsapi.cpp | 15 ++ js/src/jsprototypes.h | 14 ++ js/src/moz.build | 1 + .../tests/ecma_6/BinaryData/architecture.js | 66 ++++++ js/src/tests/ecma_6/BinaryData/shell.js | 0 js/src/vm/GlobalObject.h | 15 ++ 10 files changed, 427 insertions(+) create mode 100644 js/src/builtin/BinaryData.cpp create mode 100644 js/src/builtin/BinaryData.h create mode 100644 js/src/tests/ecma_6/BinaryData/architecture.js create mode 100644 js/src/tests/ecma_6/BinaryData/shell.js diff --git a/js/src/Makefile.in b/js/src/Makefile.in index e0a96e4afd5..16db3c8c5b2 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -72,6 +72,10 @@ ifdef NIGHTLY_BUILD DEFINES += -DENABLE_PARALLEL_JS endif +ifdef NIGHTLY_BUILD +DEFINES += -DENABLE_BINARYDATA +endif + # Ion ifdef ENABLE_ION VPATH += $(srcdir)/ion diff --git a/js/src/builtin/BinaryData.cpp b/js/src/builtin/BinaryData.cpp new file mode 100644 index 00000000000..000e6a2e794 --- /dev/null +++ b/js/src/builtin/BinaryData.cpp @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtin/BinaryData.h" + +#include "jscompartment.h" +#include "jsobj.h" + +#include "jsatominlines.h" +#include "jsobjinlines.h" + +#include "vm/GlobalObject.h" + +using namespace js; + +JSBool TypeThrowError(JSContext *cx, unsigned argc, Value *vp) +{ + return ReportIsNotFunction(cx, *vp); +} + +JSBool DataThrowError(JSContext *cx, unsigned argc, Value *vp) +{ + return ReportIsNotFunction(cx, *vp); +} + +// FIXME will actually require knowing function name +JSBool createNumericBlock(JSContext *cx, unsigned argc, jsval *vp) +{ + return false; +} + +JSBool createArrayType(JSContext *cx, unsigned argc, jsval *vp) +{ + return false; +} + +JSBool createStructType(JSContext *cx, unsigned argc, jsval *vp) +{ + return false; +} + +JSBool DataInstanceUpdate(JSContext *cx, unsigned argc, jsval *vp) +{ + return false; +} + +JSBool +ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, jsval *vp) +{ + return false; +} + +bool +GlobalObject::initDataObject(JSContext *cx, Handle global) +{ + RootedObject DataProto(cx); + DataProto = NewObjectWithGivenProto(cx, &DataClass, global->getOrCreateObjectPrototype(cx), global, SingletonObject); + if (!DataProto) + return false; + + RootedAtom DataName(cx, ClassName(JSProto_Data, cx)); + RootedFunction DataCtor(cx, global->createConstructor(cx, DataThrowError, DataName, 1, JSFunction::ExtendedFinalizeKind)); + if (!DataCtor) + return false; + + if (!JS_DefineFunction(cx, DataProto, "update", DataInstanceUpdate, 1, 0)) + return false; + + if (!LinkConstructorAndPrototype(cx, DataCtor, DataProto)) + return false; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_Data, DataCtor, DataProto)) + return false; + + global->setReservedSlot(JSProto_Data, ObjectValue(*DataCtor)); + return true; +} + +bool +GlobalObject::initTypeObject(JSContext *cx, Handle global) +{ + RootedObject TypeProto(cx, global->getOrCreateDataObject(cx)); + if (!TypeProto) + return false; + + RootedAtom TypeName(cx, ClassName(JSProto_Type, cx)); + RootedFunction TypeCtor(cx, global->createConstructor(cx, TypeThrowError, TypeName, 1, JSFunction::ExtendedFinalizeKind)); + if (!TypeCtor) + return false; + + if (!LinkConstructorAndPrototype(cx, TypeCtor, TypeProto)) + return false; + + if (!DefineConstructorAndPrototype(cx, global, JSProto_Type, TypeCtor, TypeProto)) + return false; + + global->setReservedSlot(JSProto_Type, ObjectValue(*TypeCtor)); + return true; +} + +static JSObject * +SetupComplexHeirarchy(JSContext *cx, Handle global, HandleObject complexObject) +{ + // get the 'Type' constructor + RootedObject TypeObject(cx, global->getOrCreateTypeObject(cx)); + if (!TypeObject) + return NULL; + + // Set complexObject.__proto__ = Type + if (!JS_SetPrototype(cx, complexObject, TypeObject)) + return NULL; + + RootedObject DataObject(cx, global->getOrCreateDataObject(cx)); + if (!DataObject) + return NULL; + + RootedValue DataProtoVal(cx); + if (!JSObject::getProperty(cx, DataObject, DataObject, cx->names().classPrototype, &DataProtoVal)) + return NULL; + + RootedObject DataProto(cx, DataProtoVal.toObjectOrNull()); + if (!DataProto) + return NULL; + + // Set complexObject.prototype.__proto__ = Data + RootedObject prototypeObj(cx, JS_NewObject(cx, NULL, NULL, global)); + if (!LinkConstructorAndPrototype(cx, complexObject, prototypeObj)) + return NULL; + + if (!JS_SetPrototype(cx, prototypeObj, DataObject)) + return NULL; + + // Set complexObject.prototype.prototype.__proto__ = Data.prototype + // TODO does this have to actually be a Class so we can set accessor properties etc? + RootedObject prototypePrototypeObj(cx, JS_NewObject(cx, NULL, NULL, global)); + if (!LinkConstructorAndPrototype(cx, prototypeObj, prototypePrototypeObj)) + return NULL; + + if (!JS_SetPrototype(cx, prototypePrototypeObj, DataProto)) + return NULL; + + return complexObject; +} + +static JSObject * +InitComplexClasses(JSContext *cx, Handle global) +{ + // TODO FIXME use DefineConstructorAndPrototype and other + // utilities + RootedFunction ArrayTypeFun(cx, JS_DefineFunction(cx, global, "ArrayType", createArrayType, 1, 0)); + if (!ArrayTypeFun) + return NULL; + + if (!SetupComplexHeirarchy(cx, global, ArrayTypeFun)) + return NULL; + + // ArrayType.prototype.repeat + RootedValue ArrayTypePrototypeVal(cx); + if (!JSObject::getProperty(cx, ArrayTypeFun, ArrayTypeFun, cx->names().classPrototype, &ArrayTypePrototypeVal)) + return NULL; + + if (!JS_DefineFunction(cx, ArrayTypePrototypeVal.toObjectOrNull(), "repeat", ArrayTypeObject::repeat, 1, 0)) + return NULL; + + RootedFunction StructTypeFun(cx, JS_DefineFunction(cx, global, "StructType", createStructType, 1, 0)); + if (!StructTypeFun) + return NULL; + + if (!SetupComplexHeirarchy(cx, global, StructTypeFun)) + return NULL; + + return global; +} + +JSObject * +js_InitBinaryDataClasses(JSContext *cx, HandleObject obj) +{ + JS_ASSERT(obj->is()); + Rooted global(cx, &obj->as()); + +typedef float_t float32_t; +typedef double_t float64_t; +#define BINARYDATA_NUMERIC_DEFINE(type_)\ + do {\ + JSFunction *numFun = JS_DefineFunction(cx, obj, #type_, createNumericBlock, 1, 0);\ + if (!numFun)\ + return NULL;\ +\ + if (!JS_DefineProperty(cx, numFun, "bytes", INT_TO_JSVAL(sizeof(type_##_t)), JS_PropertyStub, JS_StrictPropertyStub, 0))\ + return NULL;\ + } while(0); + BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_DEFINE) +#undef BINARYDATA_NUMERIC_DEFINE + + if (!InitComplexClasses(cx, global)) + return NULL; + return global; +} diff --git a/js/src/builtin/BinaryData.h b/js/src/builtin/BinaryData.h new file mode 100644 index 00000000000..45c6e7f1ca9 --- /dev/null +++ b/js/src/builtin/BinaryData.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef builtin_BinaryData_h +#define builtin_BinaryData_h + +#include "jsapi.h" +#include "jsobj.h" +#include "jsfriendapi.h" +#include "gc/Heap.h" + +namespace js { +class Block : public gc::Cell +{ +}; + +static Class DataClass = { + "Data", + JSCLASS_HAS_CACHED_PROTO(JSProto_Data), + JS_PropertyStub, + JS_DeletePropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub +}; + +static Class TypeClass = { + "Type", + JSCLASS_HAS_CACHED_PROTO(JSProto_Type), + JS_PropertyStub, + JS_DeletePropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub +}; + + +#define BINARYDATA_FOR_EACH_NUMERIC_TYPES(macro_)\ + macro_(uint8)\ + macro_(uint16)\ + macro_(uint32)\ + macro_(uint64)\ + macro_(int8)\ + macro_(int16)\ + macro_(int32)\ + macro_(int64)\ + macro_(float32)\ + macro_(float64) + +#define BINARYDATA_NUMERIC_CLASSES(type_)\ +static Class type_##BlockClass = {\ + #type_,\ + JSCLASS_HAS_CACHED_PROTO(JSProto_##type_),\ + JS_PropertyStub, /* addProperty */\ + JS_DeletePropertyStub, /* delProperty */\ + JS_PropertyStub, /* getProperty */\ + JS_StrictPropertyStub, /* setProperty */\ + JS_EnumerateStub,\ + JS_ResolveStub,\ + JS_ConvertStub\ +}; + +BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES) + +static Class ArrayTypeClass = { + "ArrayType", + JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType), + JS_PropertyStub, + JS_DeletePropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub +}; + +static Class StructTypeClass = { + "StructType", + JSCLASS_HAS_CACHED_PROTO(JSProto_StructType), + JS_PropertyStub, + JS_DeletePropertyStub, + JS_PropertyStub, + JS_StrictPropertyStub, + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub +}; + +class ArrayTypeObject : public JSObject +{ + public: + static JSBool repeat(JSContext *cx, unsigned int argc, jsval *vp); +}; +} + +#endif /* builtin_BinaryData_h */ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 223e2ead8e4..1fc537d85a6 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -177,6 +177,14 @@ GetBuildConfiguration(JSContext *cx, unsigned argc, jsval *vp) if (!JS_SetProperty(cx, info, "parallelJS", &value)) return false; +#ifdef ENABLE_BINARYDATA + value = BooleanValue(true); +#else + value = BooleanValue(false); +#endif + if (!JS_SetProperty(cx, info, "binary-data", &value)) + return false; + *vp = ObjectValue(*info); return true; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 6621df983d8..867eb8177c1 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -53,6 +53,7 @@ #if ENABLE_YARR_JIT #include "assembler/jit/ExecutableAllocator.h" #endif +#include "builtin/BinaryData.h" #include "builtin/Eval.h" #include "builtin/Intl.h" #include "builtin/MapObject.h" @@ -1849,6 +1850,9 @@ static const JSStdName standard_class_atoms[] = { {js_InitProxyClass, EAGER_CLASS_ATOM(Proxy), OCLASP(ObjectProxy)}, #if ENABLE_INTL_API {js_InitIntlClass, EAGER_ATOM_AND_CLASP(Intl)}, +#endif +#ifdef ENABLE_BINARYDATA + {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Type)}, #endif {NULL, 0, NULL} }; @@ -1906,6 +1910,17 @@ static const JSStdName standard_class_names[] = { TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(DataView), &DataViewObject::class_}, + /* Binary Data */ +#ifdef ENABLE_BINARYDATA + {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Type)}, + {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Data)}, +#define BINARYDATA_NUMERIC_NAMES(type_)\ + {js_InitBinaryDataClasses, EAGER_CLASS_ATOM(type_), CLASP(type_##Block)}, + BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_NAMES) +#undef BINARYDATA_NUMERIC_NAMES + {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(ArrayType)}, + {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(StructType)}, +#endif {NULL, 0, NULL} }; diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h index d4f983ae69c..3fd4d57a69b 100644 --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -56,5 +56,19 @@ macro(DataView, 35, js_InitTypedArrayClasses) \ macro(ParallelArray, 36, js_InitParallelArrayClass) \ macro(Intl, 37, js_InitIntlClass) \ + macro(Type, 38, js_InitBinaryDataClasses) \ + macro(Data, 39, js_InitBinaryDataClasses) \ + macro(uint8, 40, js_InitBinaryDataClasses) \ + macro(uint16, 41, js_InitBinaryDataClasses) \ + macro(uint32, 42, js_InitBinaryDataClasses) \ + macro(uint64, 43, js_InitBinaryDataClasses) \ + macro(int8, 44, js_InitBinaryDataClasses) \ + macro(int16, 45, js_InitBinaryDataClasses) \ + macro(int32, 46, js_InitBinaryDataClasses) \ + macro(int64, 47, js_InitBinaryDataClasses) \ + macro(float32, 48, js_InitBinaryDataClasses) \ + macro(float64, 49, js_InitBinaryDataClasses) \ + macro(ArrayType, 50, js_InitBinaryDataClasses) \ + macro(StructType, 51, js_InitBinaryDataClasses) \ #endif /* jsprototypes_h */ diff --git a/js/src/moz.build b/js/src/moz.build index da56b55d142..b411206e74d 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -80,6 +80,7 @@ EXPORTS.js += [ CPP_SOURCES += [ 'ArgumentsObject.cpp', + 'BinaryData.cpp', 'BytecodeCompiler.cpp', 'BytecodeEmitter.cpp', 'CharacterEncoding.cpp', diff --git a/js/src/tests/ecma_6/BinaryData/architecture.js b/js/src/tests/ecma_6/BinaryData/architecture.js new file mode 100644 index 00000000000..456aaea94d7 --- /dev/null +++ b/js/src/tests/ecma_6/BinaryData/architecture.js @@ -0,0 +1,66 @@ +// |reftest| skip-if(!this.hasOwnProperty("Type")) +var BUGNUMBER = 578700; +var summary = 'Binary Data class diagram'; + +function assertNotEq(a, b) { + var ok = false; + try { + assertEq(a, b); + } catch(exc) { + ok = true; + } + + if (!ok) + throw new TypeError("Assertion failed: assertNotEq(" + a + " " + b + ")"); +} + +function runTests() { + print(BUGNUMBER + ": " + summary); + + assertEq(Data.__proto__, Function.prototype); + assertEq(Data.prototype.__proto__, Object.prototype); + assertEq(Data.prototype.constructor, Data); + assertEq(typeof Data.prototype.update === "function", true); + + assertEq(Type.__proto__, Function.prototype); + assertEq(Type.prototype, Data); + + var sizes = [1, 2, 4, 8, 1, 2, 4, 8, 4, 8]; + [ uint8, uint16, uint32, + uint64, int8, int16, + int32, int64, float32, float64 ].forEach(function(numType, i) { + assertEq(numType.__proto__, Function.prototype); + assertEq(numType.bytes, sizes[i]); + }); + + assertEq(ArrayType.__proto__, Type); + assertEq(ArrayType.prototype.__proto__, Type.prototype); + assertEq(typeof ArrayType.prototype.repeat === "function", true); + + assertEq(ArrayType.prototype.prototype.__proto__, Data.prototype); + + assertEq(StructType.__proto__, Type); + assertEq(StructType.prototype.__proto__, Type.prototype); + assertEq(StructType.prototype.prototype.__proto__, Data.prototype); + + // Change global 'Type' and see if things stay sane. + Type = function() { + return 42; + } + + Data = function() { + return 43; + } + + assertNotEq(ArrayType.prototype.__proto__, Type.prototype); + assertNotEq(ArrayType.prototype.prototype.__proto__, Data.prototype); + + assertNotEq(StructType.prototype.__proto__, Type.prototype); + assertNotEq(StructType.prototype.prototype.__proto__, Data.prototype); + + if (typeof reportCompare === "function") + reportCompare(true, true); + print("Tests complete"); +} + +runTests(); diff --git a/js/src/tests/ecma_6/BinaryData/shell.js b/js/src/tests/ecma_6/BinaryData/shell.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 609eca8270a..7b3a853b9e1 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -27,6 +27,9 @@ js_InitFunctionClass(JSContext *cx, js::HandleObject obj); extern JSObject * js_InitTypedArrayClasses(JSContext *cx, js::HandleObject obj); +extern JSObject * +js_InitBinaryDataClasses(JSContext *cx, js::HandleObject obj); + namespace js { class Debugger; @@ -327,6 +330,14 @@ class GlobalObject : public JSObject return &getPrototype(JSProto_Iterator).toObject(); } + JSObject *getOrCreateDataObject(JSContext *cx) { + return getOrCreateObject(cx, JSProto_Data, initDataObject); + } + + JSObject *getOrCreateTypeObject(JSContext *cx) { + return getOrCreateObject(cx, JSProto_Type, initTypeObject); + } + private: typedef bool (*ObjectInitOp)(JSContext *cx, Handle global); @@ -429,6 +440,10 @@ class GlobalObject : public JSObject static bool initNumberFormatProto(JSContext *cx, Handle global); static bool initDateTimeFormatProto(JSContext *cx, Handle global); + // Implemented in builtin/BinaryData.cpp + static bool initTypeObject(JSContext *cx, Handle global); + static bool initDataObject(JSContext *cx, Handle global); + static bool initStandardClasses(JSContext *cx, Handle global); typedef js::Vector DebuggerVector;