mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
Remove _IS_NUMERIC macro in favor of concrete funtion CPIsNumeric()
- Create Foundation/_CPFoundationUtilities.j to house global low-level utilities. - Implement CPIsNumeric as a concrete JavaScript function, preserving the exact legacy logic. - Import _CPFoundationUtilities.j in the Foundation.j umbrella manifest. - Replace _IS_NUMERIC macro calls at all call sites with CPIsNumeric().
This commit is contained in:
+2
-3
@@ -23,12 +23,11 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../Foundation/Foundation.h"
|
|
||||||
|
|
||||||
@import "CPAnimation.j"
|
@import "CPAnimation.j"
|
||||||
@import "CPControl.j"
|
@import "CPControl.j"
|
||||||
@import "CPWindow_Constants.j"
|
|
||||||
@import "CPViewAnimation.j"
|
@import "CPViewAnimation.j"
|
||||||
|
@import "CPWindow_Constants.j"
|
||||||
|
|
||||||
@global CPApp
|
@global CPApp
|
||||||
|
|
||||||
@@ -259,7 +258,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
|||||||
*/
|
*/
|
||||||
- (void)setKnobProportion:(float)aProportion
|
- (void)setKnobProportion:(float)aProportion
|
||||||
{
|
{
|
||||||
if (!_IS_NUMERIC(aProportion))
|
if (!CPIsNumeric(aProportion))
|
||||||
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric, was: "+aProportion];
|
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric, was: "+aProportion];
|
||||||
|
|
||||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||||
|
|||||||
@@ -23,13 +23,11 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../Foundation/Foundation.h"
|
|
||||||
|
|
||||||
@import "CPButtonBar.j"
|
@import "CPButtonBar.j"
|
||||||
@import "CPImage.j"
|
|
||||||
@import "CPView.j"
|
|
||||||
@import "CPCursor.j"
|
@import "CPCursor.j"
|
||||||
|
@import "CPImage.j"
|
||||||
@import "CPTrackingArea.j"
|
@import "CPTrackingArea.j"
|
||||||
|
@import "CPView.j"
|
||||||
|
|
||||||
@class CPUserDefaults
|
@class CPUserDefaults
|
||||||
@global CPApp
|
@global CPApp
|
||||||
@@ -1171,7 +1169,7 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
|||||||
// Silently ignore bad positions which could result from odd delegate responses. We don't want these
|
// Silently ignore bad positions which could result from odd delegate responses. We don't want these
|
||||||
// bad results to go into the system and cause havoc with frame sizes as the split view tries to resize
|
// bad results to go into the system and cause havoc with frame sizes as the split view tries to resize
|
||||||
// its subviews.
|
// its subviews.
|
||||||
if (_IS_NUMERIC(proposedPosition))
|
if (CPIsNumeric(proposedPosition))
|
||||||
position = proposedPosition;
|
position = proposedPosition;
|
||||||
|
|
||||||
var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
|
var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||||
@@ -1181,10 +1179,10 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
|||||||
proposedActualMin = [self _sendDelegateSplitViewConstrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex],
|
proposedActualMin = [self _sendDelegateSplitViewConstrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex],
|
||||||
proposedActualMax = [self _sendDelegateSplitViewConstrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
proposedActualMax = [self _sendDelegateSplitViewConstrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
||||||
|
|
||||||
if (_IS_NUMERIC(proposedActualMin))
|
if (CPIsNumeric(proposedActualMin))
|
||||||
actualMin = proposedActualMin;
|
actualMin = proposedActualMin;
|
||||||
|
|
||||||
if (_IS_NUMERIC(proposedActualMax))
|
if (CPIsNumeric(proposedActualMax))
|
||||||
actualMax = proposedActualMax;
|
actualMax = proposedActualMax;
|
||||||
|
|
||||||
var viewA = _arrangedSubviews[dividerIndex],
|
var viewA = _arrangedSubviews[dividerIndex],
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../Foundation/Foundation.h"
|
|
||||||
|
|
||||||
@import "CPView.j"
|
@import "CPView.j"
|
||||||
|
|
||||||
// Gravity Areas
|
// Gravity Areas
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Foundation.h"
|
|
||||||
|
|
||||||
@import "CPArray.j"
|
@import "CPArray.j"
|
||||||
@import "CPObject.j"
|
@import "CPObject.j"
|
||||||
@import "CPRange.j"
|
@import "CPRange.j"
|
||||||
@@ -79,7 +77,7 @@
|
|||||||
*/
|
*/
|
||||||
- (id)initWithIndex:(CPInteger)anIndex
|
- (id)initWithIndex:(CPInteger)anIndex
|
||||||
{
|
{
|
||||||
if (!_IS_NUMERIC(anIndex))
|
if (!CPIsNumeric(anIndex))
|
||||||
[CPException raise:CPInvalidArgumentException
|
[CPException raise:CPInvalidArgumentException
|
||||||
reason:"Invalid index"];
|
reason:"Invalid index"];
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// By Christian C. Salvadó, http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric/1830844#1830844
|
|
||||||
#define _IS_NUMERIC(n) (!isNaN(parseFloat(n)) && isFinite(n))
|
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import "_CGGeometry.j"
|
@import "_CGGeometry.j"
|
||||||
|
@import "_CPFoundationUtilities.j"
|
||||||
@import "CPArray.j"
|
@import "CPArray.j"
|
||||||
@import "CPBundle.j"
|
@import "CPBundle.j"
|
||||||
@import "CPByteCountFormatter.j"
|
@import "CPByteCountFormatter.j"
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* _CPFoundationUtilities.j
|
||||||
|
* Foundation
|
||||||
|
*
|
||||||
|
* Created by David Richardson.
|
||||||
|
* Copyright 2026, Cappuccino Project.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define _IS_NUMERIC(n) (!isNaN(parseFloat(n)) && isFinite(n))
|
||||||
|
|
||||||
|
/*
|
||||||
|
Objective-J is a strict superset of JavaScript and compiles down to a shared global runtime scope.
|
||||||
|
This file (_CPFoundationUtilities.j) is the canonical place for otherwise "homeless" low-level
|
||||||
|
utilities, stateless helpers, and former C-style preprocessor macros that do not belong to a
|
||||||
|
specific class but are required across the framework.
|
||||||
|
|
||||||
|
While modern JavaScript ecosystems (e.g., ES6 modules, bundlers) treat the global scope as something
|
||||||
|
to be strictly avoided, Cappuccino's architecture predates these paradigms. It relies entirely on
|
||||||
|
global scope sharing for its runtime and toll-free bridging with native JavaScript, much like C and
|
||||||
|
Objective-C. Therefore, injecting `CP`-prefixed functions into the global scope is the intended design
|
||||||
|
pattern here, not an anti-pattern or "pollution."
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
New compiler does not provide a pre-processor.
|
||||||
|
The macro is expanded here to a concrete function.
|
||||||
|
|
||||||
|
The alternative is inlining at call site.
|
||||||
|
On a cold call, this provides a very minor performance advantage.
|
||||||
|
Conversely, it provides the Javascript engine fewer opportunities to optimize,
|
||||||
|
which is only done when a call site is invoked.
|
||||||
|
Additionally, it depends on individual maintainers to correctly implement the call every time.
|
||||||
|
|
||||||
|
The macro is expanded here precisely to maintain identical semantics.
|
||||||
|
|
||||||
|
Every current, popular browser engine optimizes a small, hot, monomorphic function like a numeric check almost immediately:
|
||||||
|
|
||||||
|
V8 (Chrome, Edge, Opera, Brave, Node) — tiered JIT (Ignition → Sparkplug → Maglev → TurboFan).
|
||||||
|
A function called this often gets promoted within tens of calls.
|
||||||
|
SpiderMonkey (Firefox) — Baseline Interpreter → Baseline JIT → Ion. Same pattern.
|
||||||
|
JavaScriptCore (Safari, all iOS browsers, since iOS forces WebKit) — LLInt → Baseline → DFG → FTL.
|
||||||
|
|
||||||
|
All three engines specialize aggressively on exactly this shape of code: a tiny, pure, argument-type-stable function with no side effects. It is close to the ideal case for JIT optimization — the compiler will likely inline the call at the machine-code level, which is the same outcome as hand-inlining the expression, achieved automatically.
|
||||||
|
|
||||||
|
There is no browser in current popular use — desktop or mobile — where this function call would remain a meaningful cost.
|
||||||
|
Additionally, modern hardware and Javascript engines are so much faster than in 2008, when Cappuccino was conceived,
|
||||||
|
that even a cold execution of this function is trivial.
|
||||||
|
The performance objection which originally required in-lining via a macro does not exist for current targets.
|
||||||
|
|
||||||
|
Javascript, Objective-J, C, and Objective-C all lack native namespacing.
|
||||||
|
'CP' is the canonical namespace prefix used throughout Cappuccino to address potential collisions.
|
||||||
|
It is reserved by convention.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Checks if a value is a valid, finite number.
|
||||||
|
|
||||||
|
This implements the legacy `_IS_NUMERIC` behavior exactly. It returns true for
|
||||||
|
numbers and strings that can be successfully parsed into a finite number (e.g., 42, "3.14"),
|
||||||
|
and false for NaN, Infinity, null, and purely non-numeric strings.
|
||||||
|
|
||||||
|
This specific logic (parseFloat + global isFinite) is deliberately preserved to prevent
|
||||||
|
regressions in code that historically relied on its lenient string parsing, rather than
|
||||||
|
using the stricter modern ES6 `Number.isFinite()`.
|
||||||
|
|
||||||
|
TODO: Modernize this check to use ES6 `Number.isFinite()`. This is currently deferred
|
||||||
|
to maintain strict semantic continuity during the Go/Lisette toolchain migration and
|
||||||
|
requires a full audit of all call sites to ensure string coercion is no longer expected.
|
||||||
|
|
||||||
|
@param n The value to evaluate.
|
||||||
|
@return {Boolean} YES if the value is numeric, NO otherwise.
|
||||||
|
*/
|
||||||
|
function CPIsNumeric(n) {
|
||||||
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user