+static JSValueRef ObjectiveC_Constants_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
+ CYPool pool;
+ CYUTF8String name(CYPoolUTF8String(pool, context, property));
+ if (name == "nil")
+ return Instance::Make(context, nil);
+ return NULL;
+} CYCatch(NULL) }
+
+static void ObjectiveC_Constants_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
+ JSPropertyNameAccumulatorAddName(names, CYJSString("nil"));
+}
+
+#ifdef __APPLE__
+static kern_return_t CYReadMemory(task_t task, vm_address_t address, vm_size_t size, void **data) {
+ *data = reinterpret_cast<void *>(address);
+ return KERN_SUCCESS;
+}
+
+struct CYChoice {
+ std::set<Class> query_;
+ JSContextRef context_;
+ JSObjectRef results_;
+};
+
+struct CYObjectStruct {
+ Class isa_;
+};
+
+static void choose_(task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count) {
+ CYChoice *choice(reinterpret_cast<CYChoice *>(baton));
+ JSContextRef context(choice->context_);
+
+ for (unsigned i(0); i != count; ++i) {
+ vm_range_t &range(ranges[i]);
+ void *data(reinterpret_cast<void *>(range.address));
+ size_t size(range.size);
+
+ if (size < sizeof(CYObjectStruct))
+ continue;
+
+ uintptr_t *pointers(reinterpret_cast<uintptr_t *>(data));
+#ifdef __arm64__
+ Class isa(reinterpret_cast<Class>(pointers[0] & 0x1fffffff8));
+#else
+ Class isa(reinterpret_cast<Class>(pointers[0]));
+#endif
+
+ std::set<Class>::const_iterator result(choice->query_.find(isa));
+ if (result == choice->query_.end())
+ continue;
+
+ // XXX: if (size < class_getInstanceSize(*result))
+ if ((class_getInstanceSize(*result) + 15) / 16 * 16 != size)
+ continue;
+ CYArrayPush(context, choice->results_, CYCastJSValue(context, reinterpret_cast<id>(data)));
+ }
+}
+
+static JSValueRef choose(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
+ if (count != 1)
+ throw CYJSError(context, "choose() takes a class argument");
+
+ CYPool pool;
+ Class _class(CYCastNSObject(&pool, context, arguments[0]));
+
+ vm_address_t *zones(NULL);
+ unsigned size(0);
+ kern_return_t error(malloc_get_all_zones(0, &CYReadMemory, &zones, &size));
+ _assert(error == KERN_SUCCESS);
+
+ JSObjectRef Array(CYGetCachedObject(context, CYJSString("Array")));
+ JSObjectRef results(_jsccall(JSObjectCallAsConstructor, context, Array, 0, NULL));
+
+ CYChoice choice;
+ choice.context_ = context;
+ choice.results_ = results;
+
+ size_t number;
+ Class *classes(CYCopyClassList(number));
+ _assert(classes != NULL);
+
+ for (size_t i(0); i != number; ++i)
+ for (Class current(classes[i]); current != Nil; current = class_getSuperclass(current))
+ if (current == _class) {
+ choice.query_.insert(classes[i]);
+ break;
+ }
+
+ free(classes);
+
+ for (unsigned i(0); i != size; ++i) {
+ const malloc_zone_t *zone(reinterpret_cast<const malloc_zone_t *>(zones[i]));
+ if (zone == NULL || zone->introspect == NULL)
+ continue;
+
+ zone->introspect->enumerator(mach_task_self(), &choice, MALLOC_PTR_IN_USE_RANGE_TYPE, zones[i], &CYReadMemory, &choose_);
+ }
+
+ return results;
+} CYCatch(NULL) }
+#endif
+