Skip to content

Commit 050cb5b

Browse files
committed
init
1 parent e40e9eb commit 050cb5b

File tree

1 file changed

+94
-2
lines changed
  • packages/svelte/src/compiler/phases

1 file changed

+94
-2
lines changed

packages/svelte/src/compiler/phases/scope.js

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { is_reserved, is_rune } from '../../utils.js';
1616
import { determine_slot } from '../utils/slot.js';
1717
import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js';
18+
import { regex_is_valid_identifier } from './patterns.js';
1819

1920
const 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]>} */
2526
const 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+
};
87122
export 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

Comments
 (0)