Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* |
michael@0 | 8 | * JS boolean implementation. |
michael@0 | 9 | */ |
michael@0 | 10 | |
michael@0 | 11 | #include "jsboolinlines.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "jsapi.h" |
michael@0 | 14 | #include "jsatom.h" |
michael@0 | 15 | #include "jscntxt.h" |
michael@0 | 16 | #include "jsobj.h" |
michael@0 | 17 | #include "jstypes.h" |
michael@0 | 18 | |
michael@0 | 19 | #include "vm/GlobalObject.h" |
michael@0 | 20 | #include "vm/ProxyObject.h" |
michael@0 | 21 | #include "vm/StringBuffer.h" |
michael@0 | 22 | |
michael@0 | 23 | #include "vm/BooleanObject-inl.h" |
michael@0 | 24 | |
michael@0 | 25 | using namespace js; |
michael@0 | 26 | using namespace js::types; |
michael@0 | 27 | |
michael@0 | 28 | const Class BooleanObject::class_ = { |
michael@0 | 29 | "Boolean", |
michael@0 | 30 | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), |
michael@0 | 31 | JS_PropertyStub, /* addProperty */ |
michael@0 | 32 | JS_DeletePropertyStub, /* delProperty */ |
michael@0 | 33 | JS_PropertyStub, /* getProperty */ |
michael@0 | 34 | JS_StrictPropertyStub, /* setProperty */ |
michael@0 | 35 | JS_EnumerateStub, |
michael@0 | 36 | JS_ResolveStub, |
michael@0 | 37 | JS_ConvertStub |
michael@0 | 38 | }; |
michael@0 | 39 | |
michael@0 | 40 | MOZ_ALWAYS_INLINE bool |
michael@0 | 41 | IsBoolean(HandleValue v) |
michael@0 | 42 | { |
michael@0 | 43 | return v.isBoolean() || (v.isObject() && v.toObject().is<BooleanObject>()); |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | #if JS_HAS_TOSOURCE |
michael@0 | 47 | MOZ_ALWAYS_INLINE bool |
michael@0 | 48 | bool_toSource_impl(JSContext *cx, CallArgs args) |
michael@0 | 49 | { |
michael@0 | 50 | HandleValue thisv = args.thisv(); |
michael@0 | 51 | JS_ASSERT(IsBoolean(thisv)); |
michael@0 | 52 | |
michael@0 | 53 | bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().as<BooleanObject>().unbox(); |
michael@0 | 54 | |
michael@0 | 55 | StringBuffer sb(cx); |
michael@0 | 56 | if (!sb.append("(new Boolean(") || !BooleanToStringBuffer(b, sb) || !sb.append("))")) |
michael@0 | 57 | return false; |
michael@0 | 58 | |
michael@0 | 59 | JSString *str = sb.finishString(); |
michael@0 | 60 | if (!str) |
michael@0 | 61 | return false; |
michael@0 | 62 | args.rval().setString(str); |
michael@0 | 63 | return true; |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | static bool |
michael@0 | 67 | bool_toSource(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 68 | { |
michael@0 | 69 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 70 | return CallNonGenericMethod<IsBoolean, bool_toSource_impl>(cx, args); |
michael@0 | 71 | } |
michael@0 | 72 | #endif |
michael@0 | 73 | |
michael@0 | 74 | MOZ_ALWAYS_INLINE bool |
michael@0 | 75 | bool_toString_impl(JSContext *cx, CallArgs args) |
michael@0 | 76 | { |
michael@0 | 77 | HandleValue thisv = args.thisv(); |
michael@0 | 78 | JS_ASSERT(IsBoolean(thisv)); |
michael@0 | 79 | |
michael@0 | 80 | bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().as<BooleanObject>().unbox(); |
michael@0 | 81 | args.rval().setString(js_BooleanToString(cx, b)); |
michael@0 | 82 | return true; |
michael@0 | 83 | } |
michael@0 | 84 | |
michael@0 | 85 | static bool |
michael@0 | 86 | bool_toString(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 87 | { |
michael@0 | 88 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 89 | return CallNonGenericMethod<IsBoolean, bool_toString_impl>(cx, args); |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | MOZ_ALWAYS_INLINE bool |
michael@0 | 93 | bool_valueOf_impl(JSContext *cx, CallArgs args) |
michael@0 | 94 | { |
michael@0 | 95 | HandleValue thisv = args.thisv(); |
michael@0 | 96 | JS_ASSERT(IsBoolean(thisv)); |
michael@0 | 97 | |
michael@0 | 98 | bool b = thisv.isBoolean() ? thisv.toBoolean() : thisv.toObject().as<BooleanObject>().unbox(); |
michael@0 | 99 | args.rval().setBoolean(b); |
michael@0 | 100 | return true; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | static bool |
michael@0 | 104 | bool_valueOf(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 105 | { |
michael@0 | 106 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 107 | return CallNonGenericMethod<IsBoolean, bool_valueOf_impl>(cx, args); |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | static const JSFunctionSpec boolean_methods[] = { |
michael@0 | 111 | #if JS_HAS_TOSOURCE |
michael@0 | 112 | JS_FN(js_toSource_str, bool_toSource, 0, 0), |
michael@0 | 113 | #endif |
michael@0 | 114 | JS_FN(js_toString_str, bool_toString, 0, 0), |
michael@0 | 115 | JS_FS_END |
michael@0 | 116 | }; |
michael@0 | 117 | |
michael@0 | 118 | static bool |
michael@0 | 119 | Boolean(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 120 | { |
michael@0 | 121 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 122 | |
michael@0 | 123 | bool b = args.length() != 0 ? JS::ToBoolean(args[0]) : false; |
michael@0 | 124 | |
michael@0 | 125 | if (args.isConstructing()) { |
michael@0 | 126 | JSObject *obj = BooleanObject::create(cx, b); |
michael@0 | 127 | if (!obj) |
michael@0 | 128 | return false; |
michael@0 | 129 | args.rval().setObject(*obj); |
michael@0 | 130 | } else { |
michael@0 | 131 | args.rval().setBoolean(b); |
michael@0 | 132 | } |
michael@0 | 133 | return true; |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | JSObject * |
michael@0 | 137 | js_InitBooleanClass(JSContext *cx, HandleObject obj) |
michael@0 | 138 | { |
michael@0 | 139 | JS_ASSERT(obj->isNative()); |
michael@0 | 140 | |
michael@0 | 141 | Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); |
michael@0 | 142 | |
michael@0 | 143 | RootedObject booleanProto (cx, global->createBlankPrototype(cx, &BooleanObject::class_)); |
michael@0 | 144 | if (!booleanProto) |
michael@0 | 145 | return nullptr; |
michael@0 | 146 | booleanProto->setFixedSlot(BooleanObject::PRIMITIVE_VALUE_SLOT, BooleanValue(false)); |
michael@0 | 147 | |
michael@0 | 148 | RootedFunction ctor(cx, global->createConstructor(cx, Boolean, cx->names().Boolean, 1)); |
michael@0 | 149 | if (!ctor) |
michael@0 | 150 | return nullptr; |
michael@0 | 151 | |
michael@0 | 152 | if (!LinkConstructorAndPrototype(cx, ctor, booleanProto)) |
michael@0 | 153 | return nullptr; |
michael@0 | 154 | |
michael@0 | 155 | if (!DefinePropertiesAndBrand(cx, booleanProto, nullptr, boolean_methods)) |
michael@0 | 156 | return nullptr; |
michael@0 | 157 | |
michael@0 | 158 | Handle<PropertyName*> valueOfName = cx->names().valueOf; |
michael@0 | 159 | RootedFunction |
michael@0 | 160 | valueOf(cx, NewFunction(cx, NullPtr(), bool_valueOf, 0, JSFunction::NATIVE_FUN, |
michael@0 | 161 | global, valueOfName)); |
michael@0 | 162 | if (!valueOf) |
michael@0 | 163 | return nullptr; |
michael@0 | 164 | |
michael@0 | 165 | RootedValue value(cx, ObjectValue(*valueOf)); |
michael@0 | 166 | if (!JSObject::defineProperty(cx, booleanProto, valueOfName, value, |
michael@0 | 167 | JS_PropertyStub, JS_StrictPropertyStub, 0)) |
michael@0 | 168 | { |
michael@0 | 169 | return nullptr; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Boolean, ctor, booleanProto)) |
michael@0 | 173 | return nullptr; |
michael@0 | 174 | |
michael@0 | 175 | return booleanProto; |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | JSString * |
michael@0 | 179 | js_BooleanToString(ExclusiveContext *cx, bool b) |
michael@0 | 180 | { |
michael@0 | 181 | return b ? cx->names().true_ : cx->names().false_; |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | JS_PUBLIC_API(bool) |
michael@0 | 185 | js::ToBooleanSlow(HandleValue v) |
michael@0 | 186 | { |
michael@0 | 187 | if (v.isString()) |
michael@0 | 188 | return v.toString()->length() != 0; |
michael@0 | 189 | |
michael@0 | 190 | JS_ASSERT(v.isObject()); |
michael@0 | 191 | return !EmulatesUndefined(&v.toObject()); |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | /* |
michael@0 | 195 | * This slow path is only ever taken for proxies wrapping Boolean objects |
michael@0 | 196 | * The only caller of the fast path, JSON's PreprocessValue, ensures that. |
michael@0 | 197 | */ |
michael@0 | 198 | bool |
michael@0 | 199 | js::BooleanGetPrimitiveValueSlow(HandleObject wrappedBool) |
michael@0 | 200 | { |
michael@0 | 201 | JSObject *obj = wrappedBool->as<ProxyObject>().target(); |
michael@0 | 202 | JS_ASSERT(obj); |
michael@0 | 203 | return obj->as<BooleanObject>().unbox(); |
michael@0 | 204 | } |