JSValue.h 18.2 KB
Newer Older
1 2 3
/*
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4
 *  Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
mjs's avatar
mjs committed
18
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 *  Boston, MA 02110-1301, USA.
20 21 22
 *
 */

darin@apple.com's avatar
darin@apple.com committed
23 24
#ifndef JSValue_h
#define JSValue_h
25

26
#include <math.h>
27 28
#include <stddef.h> // for size_t
#include <stdint.h>
29
#include <wtf/AlwaysInline.h>
30
#include <wtf/Assertions.h>
31
#include <wtf/HashMap.h>
32 33
#include <wtf/HashTraits.h>
#include <wtf/MathExtras.h>
34
#include <wtf/StdLibExtras.h>
darin's avatar
darin committed
35

36
namespace JSC {
37

38 39 40 41
// This is used a lot throughout JavaScriptCore for everything from value boxing to marking
// values as being missing, so it is useful to have it abbreviated.
#define QNaN (std::numeric_limits<double>::quiet_NaN())

42
    class ExecState;
barraclough@apple.com's avatar
barraclough@apple.com committed
43
    class JSCell;
44
    class JSGlobalData;
45
    class JSGlobalObject;
barraclough@apple.com's avatar
barraclough@apple.com committed
46
    class JSObject;
weinig@apple.com's avatar
weinig@apple.com committed
47
    class JSString;
48
    class PropertyName;
weinig@apple.com's avatar
weinig@apple.com committed
49
    class PropertySlot;
weinig@apple.com's avatar
weinig@apple.com committed
50
    class PutPropertySlot;
51 52
#if ENABLE(DFG_JIT)
    namespace DFG {
53
        class AssemblyHelpers;
54 55
        class JITCompiler;
        class JITCodeGenerator;
56
        class JSValueSource;
57
        class OSRExitCompiler;
58 59 60
        class SpeculativeJIT;
    }
#endif
61
#if ENABLE(LLINT_C_LOOP)
62
    namespace LLInt {
63
        class CLoop;
64
    }
65
#endif
darin@apple.com's avatar
darin@apple.com committed
66

weinig@apple.com's avatar
weinig@apple.com committed
67 68
    struct ClassInfo;
    struct Instruction;
69
    struct MethodTable;
weinig@apple.com's avatar
weinig@apple.com committed
70

71 72
    template <class T> class WriteBarrierBase;

darin@apple.com's avatar
darin@apple.com committed
73 74
    enum PreferredPrimitiveType { NoPreference, PreferNumber, PreferString };

75

76
    typedef int64_t EncodedJSValue;
77 78
    
    union EncodedValueDescriptor {
79
        int64_t asInt64;
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
#if USE(JSVALUE32_64)
        double asDouble;
#elif USE(JSVALUE64)
        JSCell* ptr;
#endif
        
#if CPU(BIG_ENDIAN)
        struct {
            int32_t tag;
            int32_t payload;
        } asBits;
#else
        struct {
            int32_t payload;
            int32_t tag;
        } asBits;
#endif
    };
98

99
    // This implements ToInt32, defined in ECMA-262 9.5.
100
    JS_EXPORT_PRIVATE int32_t toInt32(double);
101 102 103 104 105 106 107 108

    // This implements ToUInt32, defined in ECMA-262 9.6.
    inline uint32_t toUInt32(double number)
    {
        // As commented in the spec, the operation of ToInt32 and ToUint32 only differ
        // in how the result is interpreted; see NOTEs in sections 9.5 and 9.6.
        return toInt32(number);
    }
barraclough@apple.com's avatar
barraclough@apple.com committed
109

ggaren@apple.com's avatar
ggaren@apple.com committed
110
    class JSValue {
111 112 113 114
        friend struct EncodedJSValueHashTraits;
        friend class JIT;
        friend class JITStubs;
        friend class JITStubCall;
oliver@apple.com's avatar
oliver@apple.com committed
115
        friend class JSInterfaceJIT;
116
        friend class SpecializedThunkJIT;
117
#if ENABLE(DFG_JIT)
118
        friend class DFG::AssemblyHelpers;
119 120
        friend class DFG::JITCompiler;
        friend class DFG::JITCodeGenerator;
121
        friend class DFG::JSValueSource;
122
        friend class DFG::OSRExitCompiler;
123 124
        friend class DFG::SpeculativeJIT;
#endif
125 126 127
#if ENABLE(LLINT_C_LOOP)
        friend class LLInt::CLoop;
#endif
weinig@apple.com's avatar
weinig@apple.com committed
128 129

    public:
130 131 132 133 134 135 136 137 138 139 140 141
#if USE(JSVALUE32_64)
        enum { Int32Tag =        0xffffffff };
        enum { BooleanTag =      0xfffffffe };
        enum { NullTag =         0xfffffffd };
        enum { UndefinedTag =    0xfffffffc };
        enum { CellTag =         0xfffffffb };
        enum { EmptyValueTag =   0xfffffffa };
        enum { DeletedValueTag = 0xfffffff9 };

        enum { LowestTag =  DeletedValueTag };
#endif

142 143
        static EncodedJSValue encode(JSValue);
        static JSValue decode(EncodedJSValue);
144

145 146 147 148
        enum JSNullTag { JSNull };
        enum JSUndefinedTag { JSUndefined };
        enum JSTrueTag { JSTrue };
        enum JSFalseTag { JSFalse };
149
        enum EncodeAsDoubleTag { EncodeAsDouble };
150

ggaren@apple.com's avatar
ggaren@apple.com committed
151 152 153 154 155 156 157
        JSValue();
        JSValue(JSNullTag);
        JSValue(JSUndefinedTag);
        JSValue(JSTrueTag);
        JSValue(JSFalseTag);
        JSValue(JSCell* ptr);
        JSValue(const JSCell* ptr);
158

159
        // Numbers
160 161 162 163 164 165 166 167 168 169 170 171
        JSValue(EncodeAsDoubleTag, double);
        explicit JSValue(double);
        explicit JSValue(char);
        explicit JSValue(unsigned char);
        explicit JSValue(short);
        explicit JSValue(unsigned short);
        explicit JSValue(int);
        explicit JSValue(unsigned);
        explicit JSValue(long);
        explicit JSValue(unsigned long);
        explicit JSValue(long long);
        explicit JSValue(unsigned long long);
172

173
        operator bool() const;
174 175 176 177 178 179 180 181 182 183 184 185
        bool operator==(const JSValue& other) const;
        bool operator!=(const JSValue& other) const;

        bool isInt32() const;
        bool isUInt32() const;
        bool isDouble() const;
        bool isTrue() const;
        bool isFalse() const;

        int32_t asInt32() const;
        uint32_t asUInt32() const;
        double asDouble() const;
ggaren@apple.com's avatar
ggaren@apple.com committed
186 187
        bool asBoolean() const;
        double asNumber() const;
barraclough@apple.com's avatar
barraclough@apple.com committed
188

weinig@apple.com's avatar
weinig@apple.com committed
189
        // Querying the type.
190
        bool isEmpty() const;
191
        bool isFunction() const;
weinig@apple.com's avatar
weinig@apple.com committed
192 193 194 195 196 197
        bool isUndefined() const;
        bool isNull() const;
        bool isUndefinedOrNull() const;
        bool isBoolean() const;
        bool isNumber() const;
        bool isString() const;
198
        bool isPrimitive() const;
darin@apple.com's avatar
darin@apple.com committed
199
        bool isGetterSetter() const;
weinig@apple.com's avatar
weinig@apple.com committed
200
        bool isObject() const;
201
        bool inherits(const ClassInfo*) const;
weinig@apple.com's avatar
weinig@apple.com committed
202
        
weinig@apple.com's avatar
weinig@apple.com committed
203
        // Extracting the value.
204 205
        bool getString(ExecState*, WTF::String&) const;
        WTF::String getString(ExecState*) const; // null string if not a string
206
        JSObject* getObject() const; // 0 if not an object
weinig@apple.com's avatar
weinig@apple.com committed
207 208 209 210 211

        // Extracting integer values.
        bool getUInt32(uint32_t&) const;
        
        // Basic conversions.
ggaren@apple.com's avatar
ggaren@apple.com committed
212 213
        JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
        bool getPrimitiveNumber(ExecState*, double& number, JSValue&);
weinig@apple.com's avatar
weinig@apple.com committed
214

215
        bool toBoolean(ExecState*) const;
weinig@apple.com's avatar
weinig@apple.com committed
216 217 218 219

        // toNumber conversion is expected to be side effect free if an exception has
        // been set in the ExecState already.
        double toNumber(ExecState*) const;
220
        JSString* toString(ExecState*) const;
221 222
        WTF::String toWTFString(ExecState*) const;
        WTF::String toWTFStringInline(ExecState*) const;
weinig@apple.com's avatar
weinig@apple.com committed
223
        JSObject* toObject(ExecState*) const;
224
        JSObject* toObject(ExecState*, JSGlobalObject*) const;
weinig@apple.com's avatar
weinig@apple.com committed
225 226

        // Integer conversions.
227
        JS_EXPORT_PRIVATE double toInteger(ExecState*) const;
weinig@apple.com's avatar
weinig@apple.com committed
228 229 230
        double toIntegerPreserveNaN(ExecState*) const;
        int32_t toInt32(ExecState*) const;
        uint32_t toUInt32(ExecState*) const;
barraclough@apple.com's avatar
barraclough@apple.com committed
231 232 233

        // Floating point conversions (this is a convenience method for webcore;
        // signle precision float is not a representation used in JS or JSC).
barraclough@apple.com's avatar
barraclough@apple.com committed
234
        float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); }
weinig@apple.com's avatar
weinig@apple.com committed
235 236

        // Object operations, with the toObject operation included.
237 238
        JSValue get(ExecState*, PropertyName) const;
        JSValue get(ExecState*, PropertyName, PropertySlot&) const;
ggaren@apple.com's avatar
ggaren@apple.com committed
239 240
        JSValue get(ExecState*, unsigned propertyName) const;
        JSValue get(ExecState*, unsigned propertyName, PropertySlot&) const;
241 242
        void put(ExecState*, PropertyName, JSValue, PutPropertySlot&);
        void putToPrimitive(ExecState*, PropertyName, JSValue, PutPropertySlot&);
243
        void putToPrimitiveByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
244
        void putByIndex(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
weinig@apple.com's avatar
weinig@apple.com committed
245 246 247

        JSObject* toThisObject(ExecState*) const;

ggaren@apple.com's avatar
ggaren@apple.com committed
248 249 250
        static bool equal(ExecState* exec, JSValue v1, JSValue v2);
        static bool equalSlowCase(ExecState* exec, JSValue v1, JSValue v2);
        static bool equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2);
251 252 253
        static bool strictEqual(ExecState* exec, JSValue v1, JSValue v2);
        static bool strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2);
        static bool strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2);
barraclough@apple.com's avatar
barraclough@apple.com committed
254 255

        bool isCell() const;
256
        JSCell* asCell() const;
257
        JS_EXPORT_PRIVATE bool isValidCallee();
258 259
        
        JSValue structureOrUndefined() const;
mjs@apple.com's avatar
mjs@apple.com committed
260

261
        JS_EXPORT_PRIVATE char* description() const;
262

263 264
        JS_EXPORT_PRIVATE JSObject* synthesizePrototype(ExecState*) const;

weinig@apple.com's avatar
weinig@apple.com committed
265
    private:
266 267
        template <class T> JSValue(WriteBarrierBase<T>);

ggaren@apple.com's avatar
ggaren@apple.com committed
268 269 270
        enum HashTableDeletedValueTag { HashTableDeletedValue };
        JSValue(HashTableDeletedValueTag);

ggaren@apple.com's avatar
ggaren@apple.com committed
271
        inline const JSValue asValue() const { return *this; }
272
        JS_EXPORT_PRIVATE double toNumberSlowCase(ExecState*) const;
273
        JS_EXPORT_PRIVATE JSString* toStringSlowCase(ExecState*) const;
274
        JS_EXPORT_PRIVATE WTF::String toWTFStringSlowCase(ExecState*) const;
275 276
        JS_EXPORT_PRIVATE JSObject* toObjectSlowCase(ExecState*, JSGlobalObject*) const;
        JS_EXPORT_PRIVATE JSObject* toThisObjectSlowCase(ExecState*) const;
277

278
#if USE(JSVALUE32_64)
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
        /*
         * On 32-bit platforms USE(JSVALUE32_64) should be defined, and we use a NaN-encoded
         * form for immediates.
         *
         * The encoding makes use of unused NaN space in the IEEE754 representation.  Any value
         * with the top 13 bits set represents a QNaN (with the sign bit set).  QNaN values
         * can encode a 51-bit payload.  Hardware produced and C-library payloads typically
         * have a payload of zero.  We assume that non-zero payloads are available to encode
         * pointer and integer values.  Since any 64-bit bit pattern where the top 15 bits are
         * all set represents a NaN with a non-zero payload, we can use this space in the NaN
         * ranges to encode other values (however there are also other ranges of NaN space that
         * could have been selected).
         *
         * For JSValues that do not contain a double value, the high 32 bits contain the tag
         * values listed in the enums below, which all correspond to NaN-space. In the case of
294 295
         * cell, integer and bool values the lower 32 bits (the 'payload') contain the pointer
         * integer or boolean value; in the case of all other tags the payload is 0.
296
         */
297 298
        uint32_t tag() const;
        int32_t payload() const;
299 300 301 302 303 304 305 306

#if ENABLE(LLINT_C_LOOP)
        // This should only be used by the LLInt C Loop interpreter who needs
        // synthesize JSValue from its "register"s holding tag and payload
        // values.
        explicit JSValue(int32_t tag, int32_t payload);
#endif

307
#elif USE(JSVALUE64)
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
        /*
         * On 64-bit platforms USE(JSVALUE64) should be defined, and we use a NaN-encoded
         * form for immediates.
         *
         * The encoding makes use of unused NaN space in the IEEE754 representation.  Any value
         * with the top 13 bits set represents a QNaN (with the sign bit set).  QNaN values
         * can encode a 51-bit payload.  Hardware produced and C-library payloads typically
         * have a payload of zero.  We assume that non-zero payloads are available to encode
         * pointer and integer values.  Since any 64-bit bit pattern where the top 15 bits are
         * all set represents a NaN with a non-zero payload, we can use this space in the NaN
         * ranges to encode other values (however there are also other ranges of NaN space that
         * could have been selected).
         *
         * This range of NaN space is represented by 64-bit numbers begining with the 16-bit
         * hex patterns 0xFFFE and 0xFFFF - we rely on the fact that no valid double-precision
         * numbers will begin fall in these ranges.
         *
         * The top 16-bits denote the type of the encoded JSValue:
         *
         *     Pointer {  0000:PPPP:PPPP:PPPP
         *              / 0001:****:****:****
         *     Double  {         ...
         *              \ FFFE:****:****:****
         *     Integer {  FFFF:0000:IIII:IIII
         *
         * The scheme we have implemented encodes double precision values by performing a
         * 64-bit integer addition of the value 2^48 to the number. After this manipulation
         * no encoded double-precision value will begin with the pattern 0x0000 or 0xFFFF.
         * Values must be decoded by reversing this operation before subsequent floating point
         * operations my be peformed.
         *
         * 32-bit signed integers are marked with the 16-bit tag 0xFFFF.
         *
         * The tag 0x0000 denotes a pointer, or another form of tagged immediate. Boolean,
         * null and undefined values are represented by specific, invalid pointer values:
         *
         *     False:     0x06
         *     True:      0x07
         *     Undefined: 0x0a
         *     Null:      0x02
         *
         * These values have the following properties:
         * - Bit 1 (TagBitTypeOther) is set for all four values, allowing real pointers to be
         *   quickly distinguished from all immediate values, including these invalid pointers.
352
         * - With bit 3 is masked out (TagBitUndefined) Undefined and Null share the
353 354 355 356 357 358 359 360 361 362 363
         *   same value, allowing null & undefined to be quickly detected.
         *
         * No valid JSValue will have the bit pattern 0x0, this is used to represent array
         * holes, and as a C++ 'no value' result (e.g. JSValue() has an internal value of 0).
         */

        // These values are #defines since using static const integers here is a ~1% regression!

        // This value is 2^48, used to encode doubles such that the encoded value will begin
        // with a 16-bit pattern within the range 0x0001..0xFFFE.
        #define DoubleEncodeOffset 0x1000000000000ll
364 365 366 367 368
        // If all bits in the mask are set, this indicates an integer number,
        // if any but not all are set this value is a double precision number.
        #define TagTypeNumber 0xffff000000000000ll

        // All non-numeric (bool, null, undefined) immediates have bit 2 set.
369
        #define TagBitTypeOther 0x2ll
370 371 372 373 374 375 376 377
        #define TagBitBool      0x4ll
        #define TagBitUndefined 0x8ll
        // Combined integer value for non-numeric immediates.
        #define ValueFalse     (TagBitTypeOther | TagBitBool | false)
        #define ValueTrue      (TagBitTypeOther | TagBitBool | true)
        #define ValueUndefined (TagBitTypeOther | TagBitUndefined)
        #define ValueNull      (TagBitTypeOther)

378 379 380
        // TagMask is used to check for all types of immediate values (either number or 'other').
        #define TagMask (TagTypeNumber | TagBitTypeOther)

381 382 383 384 385 386 387
        // These special values are never visible to JavaScript code; Empty is used to represent
        // Array holes, and for uninitialized JSValues. Deleted is used in hash table code.
        // These values would map to cell types in the JSValue encoding, but not valid GC cell
        // pointer should have either of these values (Empty is null, deleted is at an invalid
        // alignment for a GC cell, and in the zero page).
        #define ValueEmpty   0x0ll
        #define ValueDeleted 0x4ll
388
#endif
389 390

        EncodedValueDescriptor u;
391
    };
barraclough@apple.com's avatar
barraclough@apple.com committed
392

393
    typedef IntHash<EncodedJSValue> EncodedJSValueHash;
weinig@apple.com's avatar
weinig@apple.com committed
394

395
#if USE(JSVALUE32_64)
396 397 398 399 400
    struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> {
        static const bool emptyValueIsZero = false;
        static EncodedJSValue emptyValue() { return JSValue::encode(JSValue()); }
        static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); }
        static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); }
barraclough@apple.com's avatar
barraclough@apple.com committed
401
    };
402 403
#else
    struct EncodedJSValueHashTraits : HashTraits<EncodedJSValue> {
ggaren@apple.com's avatar
ggaren@apple.com committed
404 405 406
        static void constructDeletedValue(EncodedJSValue& slot) { slot = JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); }
        static bool isDeletedValue(EncodedJSValue value) { return value == JSValue::encode(JSValue(JSValue::HashTableDeletedValue)); }
    };
407
#endif
408

409 410
    typedef HashMap<EncodedJSValue, unsigned, EncodedJSValueHash, EncodedJSValueHashTraits> JSValueMap;

ggaren@apple.com's avatar
ggaren@apple.com committed
411
    // Stand-alone helper functions.
ggaren@apple.com's avatar
ggaren@apple.com committed
412
    inline JSValue jsNull()
413
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
414
        return JSValue(JSValue::JSNull);
415 416
    }

ggaren@apple.com's avatar
ggaren@apple.com committed
417
    inline JSValue jsUndefined()
418
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
419
        return JSValue(JSValue::JSUndefined);
420 421
    }

ggaren@apple.com's avatar
ggaren@apple.com committed
422
    inline JSValue jsBoolean(bool b)
423
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
424
        return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse);
425 426
    }

427
    ALWAYS_INLINE JSValue jsDoubleNumber(double d)
428
    {
429
        ASSERT(JSValue(JSValue::EncodeAsDouble, d).isNumber());
430
        return JSValue(JSValue::EncodeAsDouble, d);
431 432
    }

433
    ALWAYS_INLINE JSValue jsNumber(double d)
434
    {
435
        ASSERT(JSValue(d).isNumber());
436
        return JSValue(d);
437 438
    }

439
    ALWAYS_INLINE JSValue jsNumber(char i)
440
    {
441
        return JSValue(i);
442 443
    }

444
    ALWAYS_INLINE JSValue jsNumber(unsigned char i)
445
    {
446
        return JSValue(i);
447 448
    }

449
    ALWAYS_INLINE JSValue jsNumber(short i)
450
    {
451
        return JSValue(i);
452 453
    }

454
    ALWAYS_INLINE JSValue jsNumber(unsigned short i)
455
    {
456
        return JSValue(i);
457 458
    }

459
    ALWAYS_INLINE JSValue jsNumber(int i)
460
    {
461
        return JSValue(i);
462 463
    }

464
    ALWAYS_INLINE JSValue jsNumber(unsigned i)
465
    {
466
        return JSValue(i);
467 468
    }

469
    ALWAYS_INLINE JSValue jsNumber(long i)
470
    {
471
        return JSValue(i);
472 473
    }

474
    ALWAYS_INLINE JSValue jsNumber(unsigned long i)
475
    {
476
        return JSValue(i);
477 478
    }

479
    ALWAYS_INLINE JSValue jsNumber(long long i)
480
    {
481
        return JSValue(i);
482 483
    }

484
    ALWAYS_INLINE JSValue jsNumber(unsigned long long i)
485
    {
486
        return JSValue(i);
487 488
    }

489 490 491 492 493 494
    inline bool operator==(const JSValue a, const JSCell* b) { return a == JSValue(b); }
    inline bool operator==(const JSCell* a, const JSValue b) { return JSValue(a) == b; }

    inline bool operator!=(const JSValue a, const JSCell* b) { return a != JSValue(b); }
    inline bool operator!=(const JSCell* a, const JSValue b) { return JSValue(a) != b; }

495
} // namespace JSC
496

darin@apple.com's avatar
darin@apple.com committed
497
#endif // JSValue_h