Make Number.isInteger an intrinsic
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 24 Feb 2018 00:48:32 +0000 (00:48 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 24 Feb 2018 00:48:32 +0000 (00:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183088

Reviewed by JF Bastien.

JSTests:

* stress/number-is-integer-intrinsic.js: Added.

Source/JavaScriptCore:

When profiling the ML subtest in ARES, I noticed it was spending some
time in Number.isInteger. This patch makes that operation an intrinsic
in the DFG/FTL. It might be a speedup by 1% or so on that subtest, but
it's likely not an aggregate speedup on ARES. However, it is definitely
faster than calling into a builtin function, so we might as well have
it as an intrinsic.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileNumberIsInteger):
(JSC::FTL::DFG::LowerDFGToB3::unboxDouble):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/NumberConstructor.cpp:
(JSC::NumberConstructor::finishCreation):
(JSC::numberConstructorFuncIsInteger):
* runtime/NumberConstructor.h:
(JSC::NumberConstructor::isIntegerImpl):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@228968 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
JSTests/ChangeLog
JSTests/stress/number-is-integer-intrinsic.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/Intrinsic.cpp
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/NumberConstructor.cpp
Source/JavaScriptCore/runtime/NumberConstructor.h

index 5b29014..26b8546 100644 (file)
@@ -1,3 +1,12 @@
+2018-02-23  Saam Barati  <sbarati@apple.com>
+
+        Make Number.isInteger an intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=183088
+
+        Reviewed by JF Bastien.
+
+        * stress/number-is-integer-intrinsic.js: Added.
+
 2018-02-23  Oleksandr Skachkov  <gskachkov@gmail.com>
 
         WebAssembly: cache memory address / size on instance
diff --git a/JSTests/stress/number-is-integer-intrinsic.js b/JSTests/stress/number-is-integer-intrinsic.js
new file mode 100644 (file)
index 0000000..788d6b2
--- /dev/null
@@ -0,0 +1,72 @@
+function assert(b) {
+    if (!b)
+        throw new Error;
+}
+
+function onlyDouble(x) {
+    return Number.isInteger(x);
+}
+noInline(onlyDouble);
+
+let interestingValues = [
+    [Infinity, false],
+    [-Infinity, false],
+    [NaN, false],
+    [0.0, true],
+    [-0.0, true],
+    [90071992547490009021129120, true],
+    [9007199254749001000, true],
+    [Number.MAX_SAFE_INTEGER, true],
+    [Number.MIN_SAFE_INTEGER, true],
+    [0.5, false],
+    [Math.PI, false],
+    [42, true],
+    [0, true],
+    [-10, true],
+    [2147483647, true],
+    [-2147483648, true],
+    [Number.MIN_VALUE, false],
+    [Number.MAX_VALUE, true],
+    [-Number.MAX_VALUE, true],
+];
+
+for (let i = 0; i < 10000; ++i) {
+    for (let [value, result] of interestingValues) {
+        assert(onlyDouble(value) === result);
+    }
+}
+
+interestingValues.push(
+    [true, false],
+    [false, false],
+    [undefined, false],
+    [null, false],
+    [{}, false],
+    [{valueOf() { throw new Error("Should not be called"); }}, false],
+    [function(){}, false],
+);
+
+function generic(x) {
+    return Number.isInteger(x);
+}
+noInline(generic);
+
+for (let i = 0; i < 10000; ++i) {
+    for (let [value, result] of interestingValues) {
+        assert(generic(value) === result);
+    }
+}
+
+function onlyInts(x) {
+    return Number.isInteger(x);
+}
+noInline(onlyInts);
+
+for (let i = 0; i < 10000; ++i) {
+    assert(onlyInts(i) === true);
+}
+for (let i = 0; i < 10000; ++i) {
+    for (let [value, result] of interestingValues) {
+        assert(onlyInts(value) === result);
+    }
+}
index ba6767d..99ed80d 100644 (file)
@@ -1,3 +1,52 @@
+2018-02-23  Saam Barati  <sbarati@apple.com>
+
+        Make Number.isInteger an intrinsic
+        https://bugs.webkit.org/show_bug.cgi?id=183088
+
+        Reviewed by JF Bastien.
+
+        When profiling the ML subtest in ARES, I noticed it was spending some
+        time in Number.isInteger. This patch makes that operation an intrinsic
+        in the DFG/FTL. It might be a speedup by 1% or so on that subtest, but
+        it's likely not an aggregate speedup on ARES. However, it is definitely
+        faster than calling into a builtin function, so we might as well have
+        it as an intrinsic.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNumberIsInteger):
+        (JSC::FTL::DFG::LowerDFGToB3::unboxDouble):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/NumberConstructor.cpp:
+        (JSC::NumberConstructor::finishCreation):
+        (JSC::numberConstructorFuncIsInteger):
+        * runtime/NumberConstructor.h:
+        (JSC::NumberConstructor::isIntegerImpl):
+
 2018-02-23  Oleksandr Skachkov  <gskachkov@gmail.com>
 
         WebAssembly: cache memory address / size on instance
index c9be93f..8ced84f 100644 (file)
@@ -36,6 +36,7 @@
 #include "HashMapImpl.h"
 #include "JITOperations.h"
 #include "MathCommon.h"
+#include "NumberConstructor.h"
 #include "Operations.h"
 #include "PutByIdStatus.h"
 #include "StringObject.h"
@@ -1176,6 +1177,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
+    case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
     case IsFunction:
@@ -1200,6 +1202,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             case IsNumber:
                 setConstant(node, jsBoolean(child.value().isNumber()));
                 break;
+            case NumberIsInteger:
+                setConstant(node, jsBoolean(NumberConstructor::isIntegerImpl(child.value())));
+                break;
             case IsObject:
                 setConstant(node, jsBoolean(child.value().isObject()));
                 break;
@@ -1307,6 +1312,22 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
             
             break;
+
+        case NumberIsInteger:
+            if (!(child.m_type & ~SpecInt32Only)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+            
+            if (!(child.m_type & SpecFullNumber)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+            
+            break;
+
         case IsObject:
             if (!(child.m_type & ~SpecObject)) {
                 setConstant(node, jsBoolean(true));
index 8db7f57..cdfb0e2 100644 (file)
@@ -3126,6 +3126,17 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         return true;
     }
 
+    case NumberIsIntegerIntrinsic: {
+        if (argumentCountIncludingThis < 2)
+            return false;
+
+        insertChecks();
+        Node* input = get(virtualRegisterForArgument(1, registerOffset));
+        Node* result = addToGraph(NumberIsInteger, input);
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+
     case CPUMfenceIntrinsic:
     case CPURdtscIntrinsic:
     case CPUCpuidIntrinsic:
index c8041a6..59577e6 100644 (file)
@@ -169,6 +169,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
+    case NumberIsInteger:
     case IsObject:
     case IsTypedArrayView:
     case LogicalNot:
index b55d78d..2cb10b2 100644 (file)
@@ -174,6 +174,7 @@ bool doesGC(Graph& graph, Node* node)
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
+    case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
     case IsFunction:
index de799ff..56ec2a9 100644 (file)
@@ -2108,6 +2108,16 @@ private:
             fixEdge<StringUse>(node->child1());
             break;
 
+        case NumberIsInteger:
+            if (node->child1()->shouldSpeculateInt32()) {
+                m_insertionSet.insertNode(
+                    m_indexInBlock, SpecNone, Check, node->origin,
+                    Edge(node->child1().node(), Int32Use));
+                m_graph.convertToConstant(node, jsBoolean(true));
+                break;
+            }
+            break;
+
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:
index 77f01a3..b5b56b2 100644 (file)
@@ -348,6 +348,7 @@ namespace JSC { namespace DFG {
     macro(IsUndefined, NodeResultBoolean) \
     macro(IsBoolean, NodeResultBoolean) \
     macro(IsNumber, NodeResultBoolean) \
+    macro(NumberIsInteger, NodeResultBoolean) \
     macro(IsObject, NodeResultBoolean) \
     macro(IsObjectOrNull, NodeResultBoolean) \
     macro(IsFunction, NodeResultBoolean) \
index 53a0a86..07b395d 100644 (file)
@@ -59,6 +59,7 @@
 #include "JSSet.h"
 #include "JSWeakMap.h"
 #include "JSWeakSet.h"
+#include "NumberConstructor.h"
 #include "ObjectConstructor.h"
 #include "Operations.h"
 #include "ParseInt.h"
@@ -2254,6 +2255,13 @@ int32_t JIT_OPERATION operationHasOwnProperty(ExecState* exec, JSObject* thisObj
     return result;
 }
 
+int32_t JIT_OPERATION operationNumberIsInteger(ExecState* exec, EncodedJSValue value)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    return NumberConstructor::isIntegerImpl(JSValue::decode(value));
+}
+
 int32_t JIT_OPERATION operationArrayIndexOfString(ExecState* exec, Butterfly* butterfly, JSString* searchElement, int32_t index)
 {
     VM& vm = exec->vm();
index adc2e45..982d26f 100644 (file)
@@ -254,6 +254,8 @@ void JIT_OPERATION operationPutDynamicVar(ExecState*, JSObject* scope, EncodedJS
 int64_t JIT_OPERATION operationConvertBoxedDoubleToInt52(EncodedJSValue);
 int64_t JIT_OPERATION operationConvertDoubleToInt52(double);
 
+int32_t JIT_OPERATION operationNumberIsInteger(ExecState*, EncodedJSValue);
+
 size_t JIT_OPERATION operationDefaultHasInstance(ExecState*, JSCell* value, JSCell* proto);
 
 char* JIT_OPERATION operationNewRawObject(ExecState*, Structure*, int32_t, Butterfly*) WTF_INTERNAL;
index e70ceb8..0e096de 100644 (file)
@@ -849,6 +849,7 @@ private:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
+        case NumberIsInteger:
         case IsObject:
         case IsObjectOrNull:
         case IsFunction:
index 7146818..02c71a5 100644 (file)
@@ -306,6 +306,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
+    case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
     case IsFunction:
index 22d438d..c64a386 100644 (file)
@@ -4405,6 +4405,17 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case NumberIsInteger: {
+        JSValueOperand input(this, node->child1());
+        JSValueRegs inputRegs = input.jsValueRegs();
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationNumberIsInteger, resultGPR, inputRegs);
+        booleanResult(resultGPR, node);
+        break;
+    }
+
     case IsObject: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this, Reuse, value, TagWord);
index 249ad6d..9293644 100644 (file)
@@ -4644,7 +4644,51 @@ void SpeculativeJIT::compile(Node* node)
         jsValueResult(result.gpr(), node, DataFormatJSBoolean);
         break;
     }
-        
+
+    case NumberIsInteger: {
+        JSValueOperand value(this, node->child1());
+        GPRTemporary result(this, Reuse, value);
+
+        FPRTemporary temp1(this);
+        FPRTemporary temp2(this);
+
+        JSValueRegs valueRegs = JSValueRegs(value.gpr());
+        GPRReg resultGPR = value.gpr();
+
+        FPRReg tempFPR1 = temp1.fpr();
+        FPRReg tempFPR2 = temp2.fpr();
+
+        MacroAssembler::JumpList done;
+
+        auto isInt32 = m_jit.branchIfInt32(valueRegs);
+        auto notNumber = m_jit.branchIfNotDoubleKnownNotInt32(valueRegs);
+
+        // We're a double here.
+        m_jit.unboxDouble(valueRegs.gpr(), resultGPR, tempFPR1);
+        m_jit.urshift64(TrustedImm32(52), resultGPR);
+        m_jit.and32(TrustedImm32(0x7ff), resultGPR);
+        auto notNanNorInfinity = m_jit.branch32(JITCompiler::NotEqual, TrustedImm32(0x7ff), resultGPR);
+        m_jit.move(TrustedImm32(ValueFalse), resultGPR);
+        done.append(m_jit.jump());
+
+        notNanNorInfinity.link(&m_jit);
+        m_jit.roundTowardZeroDouble(tempFPR1, tempFPR2);
+        m_jit.compareDouble(JITCompiler::DoubleEqual, tempFPR1, tempFPR2, resultGPR);
+        m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
+        done.append(m_jit.jump());
+
+        isInt32.link(&m_jit);
+        m_jit.move(TrustedImm32(ValueTrue), resultGPR);
+        done.append(m_jit.jump());
+
+        notNumber.link(&m_jit);
+        m_jit.move(TrustedImm32(ValueFalse), resultGPR);
+
+        done.link(&m_jit);
+        jsValueResult(resultGPR, node, DataFormatJSBoolean);
+        break;
+    }
+
     case MapHash: {
         switch (node->child1().useKind()) {
         case BooleanUse:
index 81730f9..5888149 100644 (file)
@@ -221,6 +221,7 @@ inline CapabilityLevel canCompile(Node* node)
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
+    case NumberIsInteger:
     case IsObject:
     case IsObjectOrNull:
     case IsFunction:
index 10c47d1..0ed85a8 100644 (file)
@@ -1062,6 +1062,9 @@ private:
         case IsNumber:
             compileIsNumber();
             break;
+        case NumberIsInteger:
+            compileNumberIsInteger();
+            break;
         case IsCellWithType:
             compileIsCellWithType();
             break;
@@ -8778,6 +8781,51 @@ private:
     {
         setBoolean(isNumber(lowJSValue(m_node->child1()), provenType(m_node->child1())));
     }
+
+    void compileNumberIsInteger()
+    {
+        LBasicBlock notInt32 = m_out.newBlock();
+        LBasicBlock doubleCase = m_out.newBlock();
+        LBasicBlock doubleNotNanOrInf = m_out.newBlock();
+        LBasicBlock continuation = m_out.newBlock();
+
+        LValue input = lowJSValue(m_node->child1());
+
+        ValueFromBlock trueResult = m_out.anchor(m_out.booleanTrue);
+        m_out.branch(
+            isInt32(input, provenType(m_node->child1())), unsure(continuation), unsure(notInt32));
+
+        LBasicBlock lastNext = m_out.appendTo(notInt32, doubleCase);
+        ValueFromBlock falseResult = m_out.anchor(m_out.booleanFalse);
+        m_out.branch(
+            isNotNumber(input, provenType(m_node->child1())), unsure(continuation), unsure(doubleCase));
+
+        m_out.appendTo(doubleCase, doubleNotNanOrInf);
+        LValue doubleAsInt;
+        LValue asDouble = unboxDouble(input, &doubleAsInt);
+        LValue expBits = m_out.bitAnd(m_out.lShr(doubleAsInt, m_out.constInt32(52)), m_out.constInt64(0x7ff));
+        m_out.branch(
+            m_out.equal(expBits, m_out.constInt64(0x7ff)),
+            unsure(continuation), unsure(doubleNotNanOrInf));
+
+        m_out.appendTo(doubleNotNanOrInf, continuation);
+        B3::PatchpointValue* patchpoint = m_out.patchpoint(Int32);
+        patchpoint->appendSomeRegister(asDouble);
+        patchpoint->numFPScratchRegisters = 1;
+        patchpoint->effects = Effects::none();
+        patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+            GPRReg result = params[0].gpr();
+            FPRReg input = params[1].fpr();
+            FPRReg temp = params.fpScratch(0);
+            jit.roundTowardZeroDouble(input, temp);
+            jit.compareDouble(MacroAssembler::DoubleEqual, input, temp, result);
+        });
+        ValueFromBlock patchpointResult = m_out.anchor(patchpoint);
+        m_out.jump(continuation);
+
+        m_out.appendTo(continuation, lastNext);
+        setBoolean(m_out.phi(Int32, trueResult, falseResult, patchpointResult));
+    }
     
     void compileIsCellWithType()
     {
@@ -14318,9 +14366,12 @@ private:
         return m_out.testNonZero64(jsValue, m_tagTypeNumber);
     }
 
-    LValue unboxDouble(LValue jsValue)
+    LValue unboxDouble(LValue jsValue, LValue* unboxedAsInt = nullptr)
     {
-        return m_out.bitCast(m_out.add(jsValue, m_tagTypeNumber), Double);
+        LValue asInt = m_out.add(jsValue, m_tagTypeNumber);
+        if (unboxedAsInt)
+            *unboxedAsInt = asInt;
+        return m_out.bitCast(asInt, Double);
     }
     LValue boxDouble(LValue doubleValue)
     {
index a0aa108..ad49b17 100644 (file)
@@ -129,6 +129,8 @@ const char* intrinsicName(Intrinsic intrinsic)
         return "StringPrototypeToLowerCaseIntrinsic";
     case NumberPrototypeToStringIntrinsic:
         return "NumberPrototypeToStringIntrinsic";
+    case NumberIsIntegerIntrinsic:
+        return "NumberIsIntegerIntrinsic";
     case IMulIntrinsic:
         return "IMulIntrinsic";
     case RandomIntrinsic:
index 7832e06..d64e9c7 100644 (file)
@@ -77,6 +77,7 @@ enum Intrinsic {
     StringPrototypeSliceIntrinsic,
     StringPrototypeToLowerCaseIntrinsic,
     NumberPrototypeToStringIntrinsic,
+    NumberIsIntegerIntrinsic,
     IMulIntrinsic,
     RandomIntrinsic,
     FRoundIntrinsic,
index e3cc1eb..2e7cb72 100644 (file)
@@ -47,7 +47,6 @@ const ClassInfo NumberConstructor::s_info = { "Function", &InternalFunction::s_i
 /* Source for NumberConstructor.lut.h
 @begin numberConstructorTable
   isFinite       JSBuiltin                           DontEnum|Function 1
-  isInteger      numberConstructorFuncIsInteger      DontEnum|Function 1
   isNaN          JSBuiltin                           DontEnum|Function 1
   isSafeInteger  numberConstructorFuncIsSafeInteger  DontEnum|Function 1
 @end
@@ -66,6 +65,8 @@ void NumberConstructor::finishCreation(VM& vm, NumberPrototype* numberPrototype)
     Base::finishCreation(vm, NumberPrototype::info()->className);
     ASSERT(inherits(vm, info()));
 
+    JSGlobalObject* globalObject = numberPrototype->globalObject();
+
     putDirectWithoutTransition(vm, vm.propertyNames->prototype, numberPrototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
 
@@ -80,6 +81,8 @@ void NumberConstructor::finishCreation(VM& vm, NumberPrototype* numberPrototype)
 
     putDirectWithoutTransition(vm, vm.propertyNames->parseInt, numberPrototype->globalObject()->parseIntFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
     putDirectWithoutTransition(vm, vm.propertyNames->parseFloat, numberPrototype->globalObject()->parseFloatFunction(), static_cast<unsigned>(PropertyAttribute::DontEnum));
+
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(Identifier::fromString(&vm, "isInteger"), numberConstructorFuncIsInteger, static_cast<unsigned>(PropertyAttribute::DontEnum), 1, NumberIsIntegerIntrinsic);
 }
 
 // ECMA 15.7.1
@@ -106,17 +109,7 @@ static EncodedJSValue JSC_HOST_CALL callNumberConstructor(ExecState* exec)
 // ECMA-262 20.1.2.3
 static EncodedJSValue JSC_HOST_CALL numberConstructorFuncIsInteger(ExecState* exec)
 {
-    JSValue argument = exec->argument(0);
-    bool isInteger;
-    if (argument.isInt32())
-        isInteger = true;
-    else if (!argument.isDouble())
-        isInteger = false;
-    else {
-        double number = argument.asDouble();
-        isInteger = std::isfinite(number) && trunc(number) == number;
-    }
-    return JSValue::encode(jsBoolean(isInteger));
+    return JSValue::encode(jsBoolean(NumberConstructor::isIntegerImpl(exec->argument(0))));
 }
 
 // ECMA-262 20.1.2.5
index 10f58a2..6b6776c 100644 (file)
@@ -46,6 +46,17 @@ public:
         return Structure::create(vm, globalObject, proto, TypeInfo(InternalFunctionType, StructureFlags), info()); 
     }
 
+    static bool isIntegerImpl(JSValue value)
+    {
+        if (value.isInt32())
+            return true;
+        if (!value.isDouble())
+            return false;
+
+        double number = value.asDouble();
+        return std::isfinite(number) && trunc(number) == number;
+    }
+
 protected:
     void finishCreation(VM&, NumberPrototype*);