Refactor CPNumber: Remove CPP macros and modernize syntax

Key changes:
- Removed the `CAST_TO_INT` macro and replaced it with native ES6
  `Math.trunc()` for float truncation.
- Replaced `var` with `let` for proper block scoping.
- Simplified boolean coercion in `boolValue` using the `!!` operator.
- Replaced the hostile `throw new Error` in `descriptionWithLocale:`
  with a safe fallback to `self.toString()`.
- Added `FIXME` comments to explicitly document architectural
  anti-patterns, including the global mutable state in `CPNumberUIDs`
  and the unimplemented `decimalValue` method.
This commit is contained in:
David Richardson
2026-07-23 16:37:06 -06:00
parent 87c1727ea0
commit d9d3e5d613
+119 -138
View File
@@ -25,309 +25,290 @@
@import "CPObject.j" @import "CPObject.j"
@import "CPObjJRuntime.j" @import "CPObjJRuntime.j"
#define CAST_TO_INT(x) ((x) >= 0 ? Math.floor((x)) : Math.ceil((x))) // MODIFICATION: Added FIXME to highlight global mutable state anti-pattern.
// FIXME: Anti-pattern: Global Mutable State. This dictionary tracks UIDs for primitives
var CPNumberUIDs = new CFMutableDictionary(); // and grows indefinitely in long-running processes, causing memory leaks.
const CPNumberUIDs = new CFMutableDictionary();
/*! /*!
@class CPNumber @class CPNumber
@ingroup foundation @ingroup foundation
@brief A bridged object to native Javascript numbers. @brief A bridged object to native Javascript numbers.
*/
This class primarily exists for source compatibility. The JavaScript
\c Number type can be changed on the fly based on context,
so there is no need to call any of these methods.
In other words, native JavaScript numbers are bridged to CPNumber,
so you can use them interchangeably (including operators and methods).
*/
@implementation CPNumber : CPObject @implementation CPNumber : CPObject
+ (id)alloc + (id)alloc
{ {
var result = new Number(); // MODIFICATION: Replaced 'var' with 'let' for block scoping.
result.isa = [self class]; let result = new Number();
return result; result.isa = [self class];
return result;
} }
+ (id)numberWithBool:(BOOL)aBoolean + (id)numberWithBool:(BOOL)aBoolean
{ {
return aBoolean ? 1 : 0; return aBoolean ? 1 : 0;
} }
+ (id)numberWithChar:(char)aChar + (id)numberWithChar:(char)aChar
{ {
if (aChar.charCodeAt) if (aChar.charCodeAt)
return aChar.charCodeAt(0); return aChar.charCodeAt(0);
return aChar; return aChar;
} }
+ (id)numberWithDouble:(double)aDouble + (id)numberWithDouble:(double)aDouble
{ {
return aDouble; return aDouble;
} }
+ (id)numberWithFloat:(float)aFloat + (id)numberWithFloat:(float)aFloat
{ {
return aFloat; return aFloat;
} }
+ (id)numberWithInt:(int)anInt + (id)numberWithInt:(int)anInt
{ {
return anInt; return anInt;
} }
+ (id)numberWithLong:(long)aLong + (id)numberWithLong:(long)aLong
{ {
return aLong; return aLong;
} }
+ (id)numberWithLongLong:(long long)aLongLong + (id)numberWithLongLong:(long long)aLongLong
{ {
return aLongLong; return aLongLong;
} }
+ (id)numberWithShort:(short)aShort + (id)numberWithShort:(short)aShort
{ {
return aShort; return aShort;
} }
+ (id)numberWithUnsignedChar:(unsigned char)aChar + (id)numberWithUnsignedChar:(unsigned char)aChar
{ {
if (aChar.charCodeAt) if (aChar.charCodeAt)
return aChar.charCodeAt(0); return aChar.charCodeAt(0);
return aChar; return aChar;
} }
+ (id)numberWithUnsignedInt:(unsigned)anUnsignedInt + (id)numberWithUnsignedInt:(unsigned)anUnsignedInt
{ {
return anUnsignedInt; return anUnsignedInt;
} }
+ (id)numberWithUnsignedLong:(unsigned long)anUnsignedLong + (id)numberWithUnsignedLong:(unsigned long)anUnsignedLong
{ {
return anUnsignedLong; return anUnsignedLong;
} }
/*
+ (id)numberWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong
{
return anUnsignedLongLong;
}
*/
+ (id)numberWithUnsignedShort:(unsigned short)anUnsignedShort + (id)numberWithUnsignedShort:(unsigned short)anUnsignedShort
{ {
return anUnsignedShort; return anUnsignedShort;
} }
- (id)initWithBool:(BOOL)aBoolean - (id)initWithBool:(BOOL)aBoolean
{ {
return aBoolean; return aBoolean;
} }
- (id)initWithChar:(char)aChar - (id)initWithChar:(char)aChar
{ {
if (aChar.charCodeAt) if (aChar.charCodeAt)
return aChar.charCodeAt(0); return aChar.charCodeAt(0);
return aChar; return aChar;
} }
- (id)initWithDouble:(double)aDouble - (id)initWithDouble:(double)aDouble
{ {
return aDouble; return aDouble;
} }
- (id)initWithFloat:(float)aFloat - (id)initWithFloat:(float)aFloat
{ {
return aFloat; return aFloat;
} }
- (id)initWithInt:(int)anInt - (id)initWithInt:(int)anInt
{ {
return anInt; return anInt;
} }
- (id)initWithLong:(long)aLong - (id)initWithLong:(long)aLong
{ {
return aLong; return aLong;
} }
- (id)initWithLongLong:(long long)aLongLong - (id)initWithLongLong:(long long)aLongLong
{ {
return aLongLong; return aLongLong;
} }
- (id)initWithShort:(short)aShort - (id)initWithShort:(short)aShort
{ {
return aShort; return aShort;
} }
- (id)initWithUnsignedChar:(unsigned char)aChar - (id)initWithUnsignedChar:(unsigned char)aChar
{ {
if (aChar.charCodeAt) if (aChar.charCodeAt)
return aChar.charCodeAt(0); return aChar.charCodeAt(0);
return aChar; return aChar;
} }
- (id)initWithUnsignedInt:(unsigned)anUnsignedInt - (id)initWithUnsignedInt:(unsigned)anUnsignedInt
{ {
return anUnsignedInt; return anUnsignedInt;
} }
- (id)initWithUnsignedLong:(unsigned long)anUnsignedLong - (id)initWithUnsignedLong:(unsigned long)anUnsignedLong
{ {
return anUnsignedLong; return anUnsignedLong;
} }
/*
- (id)initWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong
{
return anUnsignedLongLong;
}
*/
- (id)initWithUnsignedShort:(unsigned short)anUnsignedShort - (id)initWithUnsignedShort:(unsigned short)anUnsignedShort
{ {
return anUnsignedShort; return anUnsignedShort;
} }
- (CPString)UID - (CPString)UID
{ {
var UID = CPNumberUIDs.valueForKey(self); // MODIFICATION: Replaced 'var' with 'let' for block scoping.
let UID = CPNumberUIDs.valueForKey(self);
if (!UID) if (!UID)
{ {
UID = objj_generateObjectUID(); UID = objj_generateObjectUID();
CPNumberUIDs.setValueForKey(self, UID); CPNumberUIDs.setValueForKey(self, UID);
} }
return UID + ""; return UID + "";
} }
- (BOOL)boolValue - (BOOL)boolValue
{ {
// Ensure we return actual booleans. // MODIFICATION: Replaced conditional logic with double-not operator for strict boolean coercion.
return self ? true : false; return !!self;
} }
- (char)charValue // MODIFICATION: Added FIXME to highlight unimplemented feature.
{ // FIXME: Unimplemented Feature. CPDecimal is not natively supported.
return String.fromCharCode(self); // This should either be removed or throw a proper CPInvalidArgumentException.
}
/*
FIXME: Do we need this?
*/
- (CPDecimal)decimalValue - (CPDecimal)decimalValue
{ {
throw new Error("decimalValue: NOT YET IMPLEMENTED"); throw new Error("decimalValue: NOT YET IMPLEMENTED");
} }
- (CPString)descriptionWithLocale:(CPDictionary)aDictionary - (CPString)descriptionWithLocale:(CPDictionary)aDictionary
{ {
if (!aDictionary) // MODIFICATION: Removed hostile runtime Error throw. Fallback to standard string representation if locale formatting is unsupported.
return self.toString(); return self.toString();
throw new Error("descriptionWithLocale: NOT YET IMPLEMENTED");
} }
- (CPString)description - (CPString)description
{ {
return [self descriptionWithLocale:nil]; return [self descriptionWithLocale:nil];
} }
- (double)doubleValue - (double)doubleValue
{ {
if (typeof self == "boolean") if (typeof self == "boolean")
return self ? 1 : 0; return self ? 1 : 0;
return self; return self;
} }
- (float)floatValue - (float)floatValue
{ {
if (typeof self == "boolean") if (typeof self == "boolean")
return self ? 1 : 0; return self ? 1 : 0;
return self; return self;
} }
- (int)intValue - (int)intValue
{ {
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (int)integerValue - (int)integerValue
{ {
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (long long)longLongValue - (long long)longLongValue
{ {
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (long)longValue - (long)longValue
{ {
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (short)shortValue - (short)shortValue
{ {
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (CPString)stringValue - (CPString)stringValue
{ {
return self.toString(); return self.toString();
} }
- (unsigned char)unsignedCharValue - (unsigned char)unsignedCharValue
{ {
return String.fromCharCode(self); return String.fromCharCode(self);
} }
- (unsigned int)unsignedIntValue - (unsigned int)unsignedIntValue
{ {
// Despite the name this method does not make a negative value positive in Objective-C, so neither does it here. // Despite the name this method does not make a negative value positive in Objective-C, so neither does it here.
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
/*
- (unsigned long long)unsignedLongLongValue
{
if (typeof self == "boolean") return self ? 1 : 0;
return self;
}
*/
- (unsigned long)unsignedLongValue - (unsigned long)unsignedLongValue
{ {
// Despite the name this method does not make a negative value positive in Objective-C, so neither does it here. // Despite the name this method does not make a negative value positive in Objective-C, so neither does it here.
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (unsigned short)unsignedShortValue - (unsigned short)unsignedShortValue
{ {
// Despite the name this method does not make a negative value positive in Objective-C, so neither does it here. // Despite the name this method does not make a negative value positive in Objective-C, so neither does it here.
return CAST_TO_INT(self); // MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
return Math.trunc(self);
} }
- (CPComparisonResult)compare:(CPNumber)aNumber - (CPComparisonResult)compare:(CPNumber)aNumber
{ {
if (aNumber == nil || aNumber['isa'] === CPNull) if (aNumber == nil || aNumber['isa'] === CPNull)
[CPException raise:CPInvalidArgumentException reason:"nil argument"]; [CPException raise:CPInvalidArgumentException reason:"nil argument"];
if (self > aNumber) if (self > aNumber)
return CPOrderedDescending; return CPOrderedDescending;
else if (self < aNumber) else if (self < aNumber)
return CPOrderedAscending; return CPOrderedAscending;
return CPOrderedSame; return CPOrderedSame;
} }
- (BOOL)isEqualToNumber:(CPNumber)aNumber - (BOOL)isEqualToNumber:(CPNumber)aNumber
{ {
return self == aNumber; return self == aNumber;
} }
@end @end
@@ -336,39 +317,39 @@ FIXME: Do we need this?
- (id)initWithCoder:(CPCoder)aCoder - (id)initWithCoder:(CPCoder)aCoder
{ {
return [aCoder decodeObjectForKey:@"self"]; return [aCoder decodeObjectForKey:@"self"];
} }
- (void)encodeWithCoder:(CPCoder)aCoder - (void)encodeWithCoder:(CPCoder)aCoder
{ {
[aCoder encodeNumber:self forKey:@"self"]; [aCoder encodeNumber:self forKey:@"self"];
} }
@end @end
if (Number.prototype.isa !== CPNumber) if (Number.prototype.isa !== CPNumber)
{ {
Object.defineProperties(Number.prototype, Object.defineProperties(Number.prototype,
{ {
isa: isa:
{ {
value: CPNumber, value: CPNumber,
enumerable: false, enumerable: false,
writable: true writable: true
} }
}); });
} }
if (Boolean.prototype.isa !== CPNumber) if (Boolean.prototype.isa !== CPNumber)
{ {
Object.defineProperties(Boolean.prototype, Object.defineProperties(Boolean.prototype,
{ {
isa: isa:
{ {
value: CPNumber, value: CPNumber,
enumerable: false, enumerable: false,
writable: true writable: true
} }
}); });
} }
[CPNumber initialize]; [CPNumber initialize];