@@ -15,6 +15,7 @@ import {
1515import { is_reserved , is_rune } from '../../utils.js' ;
1616import { determine_slot } from '../utils/slot.js' ;
1717import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js' ;
18+ import { regex_is_valid_identifier } from './patterns.js' ;
1819
1920const UNKNOWN = Symbol ( 'unknown' ) ;
2021/** Includes `BigInt` */
@@ -24,11 +25,12 @@ export const STRING = Symbol('string');
2425/** @type {Record<string, [type: NUMBER | STRING | UNKNOWN, fn?: Function]> } */
2526const globals = {
2627 BigInt : [ NUMBER , BigInt ] ,
28+ 'Date.now' : [ NUMBER ] ,
2729 'Math.min' : [ NUMBER , Math . min ] ,
2830 'Math.max' : [ NUMBER , Math . max ] ,
2931 'Math.random' : [ NUMBER ] ,
3032 'Math.floor' : [ NUMBER , Math . floor ] ,
31- // @ts -expect-error
33+ // @ts -ignore
3234 'Math.f16round' : [ NUMBER , Math . f16round ] ,
3335 'Math.round' : [ NUMBER , Math . round ] ,
3436 'Math.abs' : [ NUMBER , Math . abs ] ,
@@ -84,6 +86,39 @@ const global_constants = {
8486 'Math.SQRT1_2' : Math . SQRT1_2
8587} ;
8688
89+ /**
90+ * @template T
91+ * @param {(...args: any) => T } fn
92+ * @returns {(this: unknown, ...args: any) => T }
93+ */
94+ function call_bind ( fn ) {
95+ return /** @type {(this: unknown, ...args: any) => T } */ ( fn . call . bind ( fn ) ) ;
96+ }
97+
98+ const string_proto = String . prototype ;
99+ const number_proto = Number . prototype ;
100+
101+ /** @type {Record<string, Record<string, [type: NUMBER | STRING | UNKNOWN, fn?: Function]>> } */
102+ const prototype_methods = {
103+ string : {
104+ //@ts -ignore
105+ toString : [ STRING , call_bind ( string_proto . toString ) ] ,
106+ toLowerCase : [ STRING , call_bind ( string_proto . toLowerCase ) ] ,
107+ toUpperCase : [ STRING , call_bind ( string_proto . toUpperCase ) ] ,
108+ slice : [ STRING , call_bind ( string_proto . slice ) ] ,
109+ at : [ STRING , call_bind ( string_proto . at ) ] ,
110+ charAt : [ STRING , call_bind ( string_proto . charAt ) ] ,
111+ trim : [ STRING , call_bind ( string_proto . trim ) ] ,
112+ indexOf : [ STRING , call_bind ( string_proto . indexOf ) ]
113+ } ,
114+ number : {
115+ //@ts -ignore
116+ toString : [ STRING , call_bind ( number_proto . toString ) ] ,
117+ toFixed : [ NUMBER , call_bind ( number_proto . toFixed ) ] ,
118+ toExponential : [ NUMBER , call_bind ( number_proto . toExponential ) ] ,
119+ toPrecision : [ NUMBER , call_bind ( number_proto . toPrecision ) ]
120+ }
121+ } ;
87122export class Binding {
88123 /** @type {Scope } */
89124 scope ;
@@ -462,6 +497,53 @@ class Evaluation {
462497 this . values . add ( type ) ;
463498 }
464499
500+ break ;
501+ }
502+ } else if (
503+ expression . callee . type === 'MemberExpression' &&
504+ expression . callee . object . type !== 'Super' &&
505+ expression . arguments . every ( ( arg ) => arg . type !== 'SpreadElement' )
506+ ) {
507+ const object = scope . evaluate ( expression . callee . object ) ;
508+ if ( ! object . is_known ) {
509+ this . values . add ( UNKNOWN ) ;
510+ break ;
511+ }
512+ let property ;
513+ if (
514+ expression . callee . computed &&
515+ expression . callee . property . type !== 'PrivateIdentifier'
516+ ) {
517+ property = scope . evaluate ( expression . callee . property ) ;
518+ if ( property . is_known ) {
519+ property = property . value ;
520+ } else {
521+ this . values . add ( UNKNOWN ) ;
522+ break ;
523+ }
524+ } else if ( expression . callee . property . type === 'Identifier' ) {
525+ property = expression . callee . property . name ;
526+ }
527+ if ( property === undefined ) {
528+ this . values . add ( UNKNOWN ) ;
529+ break ;
530+ }
531+ if ( typeof object . value !== 'string' && typeof object . value !== 'number' ) {
532+ this . values . add ( UNKNOWN ) ;
533+ break ;
534+ }
535+ const available_methods =
536+ prototype_methods [ /** @type {'string' | 'number' } */ ( typeof object . value ) ] ;
537+ if ( Object . hasOwn ( available_methods , property ) ) {
538+ const [ type , fn ] = available_methods [ property ] ;
539+ console . log ( [ type , fn ] ) ;
540+ const values = expression . arguments . map ( ( arg ) => scope . evaluate ( arg ) ) ;
541+
542+ if ( fn && values . every ( ( e ) => e . is_known ) ) {
543+ this . values . add ( fn ( object . value , ...values . map ( ( e ) => e . value ) ) ) ;
544+ } else {
545+ this . values . add ( type ) ;
546+ }
465547 break ;
466548 }
467549 }
@@ -1296,7 +1378,17 @@ function get_global_keypath(node, scope) {
12961378 let joined = '' ;
12971379
12981380 while ( n . type === 'MemberExpression' ) {
1299- if ( n . computed ) return null ;
1381+ if ( n . computed && n . property . type !== 'PrivateIdentifier' ) {
1382+ const property = scope . evaluate ( n . property ) ;
1383+ if ( property . is_known ) {
1384+ if ( ! regex_is_valid_identifier . test ( property . value ) ) {
1385+ return null ;
1386+ }
1387+ joined = '.' + property . value + joined ;
1388+ n = n . object ;
1389+ continue ;
1390+ }
1391+ }
13001392 if ( n . property . type !== 'Identifier' ) return null ;
13011393 joined = '.' + n . property . name + joined ;
13021394 n = n . object ;
0 commit comments