feat(codemode): math parity (#35609)
This commit is contained in:
parent
df471872de
commit
ba24037e64
6 changed files with 172 additions and 18 deletions
|
|
@ -4,6 +4,13 @@ export const mathMethods = new Set([
|
|||
"max",
|
||||
"min",
|
||||
"abs",
|
||||
"acos",
|
||||
"acosh",
|
||||
"asin",
|
||||
"asinh",
|
||||
"atan",
|
||||
"atan2",
|
||||
"atanh",
|
||||
"floor",
|
||||
"ceil",
|
||||
"round",
|
||||
|
|
@ -13,10 +20,22 @@ export const mathMethods = new Set([
|
|||
"cbrt",
|
||||
"pow",
|
||||
"hypot",
|
||||
"cos",
|
||||
"cosh",
|
||||
"sin",
|
||||
"sinh",
|
||||
"tan",
|
||||
"tanh",
|
||||
"log",
|
||||
"log2",
|
||||
"log10",
|
||||
"log1p",
|
||||
"exp",
|
||||
"expm1",
|
||||
"f16round",
|
||||
"fround",
|
||||
"clz32",
|
||||
"imul",
|
||||
])
|
||||
|
||||
export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNode): number => {
|
||||
|
|
@ -33,6 +52,20 @@ export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNo
|
|||
return Math.min(...nums)
|
||||
case "abs":
|
||||
return Math.abs(a)
|
||||
case "acos":
|
||||
return Math.acos(a)
|
||||
case "acosh":
|
||||
return Math.acosh(a)
|
||||
case "asin":
|
||||
return Math.asin(a)
|
||||
case "asinh":
|
||||
return Math.asinh(a)
|
||||
case "atan":
|
||||
return Math.atan(a)
|
||||
case "atan2":
|
||||
return Math.atan2(a, b)
|
||||
case "atanh":
|
||||
return Math.atanh(a)
|
||||
case "floor":
|
||||
return Math.floor(a)
|
||||
case "ceil":
|
||||
|
|
@ -51,14 +84,38 @@ export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNo
|
|||
return Math.pow(a, b)
|
||||
case "hypot":
|
||||
return Math.hypot(...nums)
|
||||
case "cos":
|
||||
return Math.cos(a)
|
||||
case "cosh":
|
||||
return Math.cosh(a)
|
||||
case "sin":
|
||||
return Math.sin(a)
|
||||
case "sinh":
|
||||
return Math.sinh(a)
|
||||
case "tan":
|
||||
return Math.tan(a)
|
||||
case "tanh":
|
||||
return Math.tanh(a)
|
||||
case "log":
|
||||
return Math.log(a)
|
||||
case "log2":
|
||||
return Math.log2(a)
|
||||
case "log10":
|
||||
return Math.log10(a)
|
||||
case "log1p":
|
||||
return Math.log1p(a)
|
||||
case "exp":
|
||||
return Math.exp(a)
|
||||
case "expm1":
|
||||
return Math.expm1(a)
|
||||
case "f16round":
|
||||
return Math.f16round(a)
|
||||
case "fround":
|
||||
return Math.fround(a)
|
||||
case "clz32":
|
||||
return Math.clz32(a)
|
||||
case "imul":
|
||||
return Math.imul(a, b)
|
||||
}
|
||||
throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
|
||||
import { isBlockedMember } from "../tool-runtime.js"
|
||||
import { isSandboxValue, SandboxMap, SandboxURLSearchParams } from "../values.js"
|
||||
import { isSandboxValue, SandboxMap, SandboxSet, SandboxURLSearchParams } from "../values.js"
|
||||
import { boundedData, coerceToString } from "./value.js"
|
||||
|
||||
export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "assign", "fromEntries"])
|
||||
export const objectMethodsPreservingIdentity = new Set(["assign", "values", "entries", "fromEntries"])
|
||||
|
||||
export const invokeObjectMethod = (name: string, args: Array<unknown>, node: AstNode): unknown => {
|
||||
if (!objectStatics.has(name)) throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
|
||||
const requireObject = (): Record<string, unknown> => {
|
||||
const input = args[0]
|
||||
const value = boundedData(args[0], `Object.${name} input`)
|
||||
if (Array.isArray(input)) return input as unknown as Record<string, unknown>
|
||||
if (isSandboxValue(value)) return {}
|
||||
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new InterpreterRuntimeError(`Object.${name} expects a data object.`, node)
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node)
|
||||
}
|
||||
return value as Record<string, unknown>
|
||||
}
|
||||
|
|
@ -19,6 +22,11 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
|
|||
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
|
||||
out[key] = item
|
||||
}
|
||||
const addEntry = (out: Record<string, unknown>, key: unknown, item: unknown): void => {
|
||||
boundedData(key, "Object.fromEntries key")
|
||||
boundedData(item, "Object.fromEntries value")
|
||||
guardedSet(out, coerceToString(key), item)
|
||||
}
|
||||
switch (name) {
|
||||
case "keys": {
|
||||
const value = boundedData(args[0], "Object.keys input")
|
||||
|
|
@ -55,7 +63,7 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
|
|||
case "fromEntries": {
|
||||
if (args[0] instanceof SandboxMap) {
|
||||
const out: Record<string, unknown> = Object.create(null)
|
||||
for (const [key, item] of args[0].map.entries()) guardedSet(out, coerceToString(key), item)
|
||||
for (const [key, item] of args[0].map.entries()) addEntry(out, key, item)
|
||||
return out
|
||||
}
|
||||
if (args[0] instanceof SandboxURLSearchParams) {
|
||||
|
|
@ -63,16 +71,18 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
|
|||
for (const [key, value] of args[0].params.entries()) guardedSet(out, key, value)
|
||||
return out
|
||||
}
|
||||
const pairs = boundedData(args[0], "Object.fromEntries input")
|
||||
const pairs = args[0] instanceof SandboxSet ? Array.from(args[0].set.values()) : args[0]
|
||||
if (!Array.isArray(pairs)) {
|
||||
boundedData(args[0], "Object.fromEntries input")
|
||||
throw new InterpreterRuntimeError("Object.fromEntries expects an array of [key, value] pairs.", node)
|
||||
}
|
||||
const out: Record<string, unknown> = Object.create(null)
|
||||
for (const pair of pairs) {
|
||||
if (!Array.isArray(pair)) {
|
||||
throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] pairs.", node)
|
||||
}
|
||||
guardedSet(out, String(pair[0]), pair[1])
|
||||
const validated = boundedData(pair, "Object.fromEntries entry")
|
||||
if (validated === null || typeof validated !== "object" || isSandboxValue(validated))
|
||||
throw new InterpreterRuntimeError("Object.fromEntries expects [key, value] entry objects.", node)
|
||||
const entry = pair as Record<string, unknown>
|
||||
addEntry(out, entry[0], entry[1])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue