From 58e3546459c8488897fcc0f0a51ce55de02d3bb4 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:22:06 +0200 Subject: [PATCH] implement __proto__ field for all objects --- src/lib/libs/values/object.ts | 208 ++++++++++++++++++ .../compilation/members/FieldMemberNode.java | 2 +- 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/lib/libs/values/object.ts diff --git a/src/lib/libs/values/object.ts b/src/lib/libs/values/object.ts new file mode 100644 index 0000000..bae22bc --- /dev/null +++ b/src/lib/libs/values/object.ts @@ -0,0 +1,208 @@ +import { Boolean } from "./boolean.ts"; +import { TypeError } from "./errors.ts"; +import { Number } from "./number.ts"; +import { func, object } from "../primordials.ts"; +import { String } from "./string.ts"; +import { symbols, valueKey } from "../utils.ts"; +import { Symbol } from "../values/symbol.ts"; + +export const Object = (() => { + class Object { + public toString(this: unknown) { + if (this === undefined) return "[object Undefined]"; + else if (this === null) return "[object Null]"; + else if (typeof this === "object") { + if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]"; + else if (object.isArray(this)) return "[object Array]"; + else return "[object Object]"; + } + else if (typeof this === "number" || this instanceof Number) return "[object Number]"; + else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; + else if (typeof this === "string" || this instanceof String) return "[object String]"; + else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; + else if (typeof this === "function") return "[object Function]"; + } + public valueOf() { + return this; + } + public hasOwnProperty(key: string) { + return object.getOwnMember(this, key) != null; + } + + public get __proto__() { + return object.getPrototype(this); + } + public set __proto__(val) { + object.setPrototype(this, val); + } + + public constructor (value?: unknown) { + if (typeof value === 'object' && value !== null) return value as any; + if (typeof value === 'string') return new String(value) as any; + if (typeof value === 'number') return new Number(value) as any; + if (typeof value === 'boolean') return new Boolean(value) as any; + if (typeof value === 'symbol') { + const res: Symbol = {} as any; + object.setPrototype(res, Symbol.prototype); + res[valueKey] = value; + return res as any; + } + + return {} as any; + } + + public static getOwnPropertyDescriptor(obj: object, key: any) { + return object.getOwnMember(obj, key); + } + public static getOwnPropertyNames(obj: object): string[] { + return object.getOwnMembers(obj, false); + } + public static getOwnPropertySymbols(obj: object): symbol[] { + return object.getOwnSymbolMembers(obj, false); + } + + public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) { + if (obj === null || typeof obj !== "function" && typeof obj !== "object") { + throw new TypeError("Object.defineProperty called on non-object"); + } + if (desc === null || typeof desc !== "function" && typeof desc !== "object") { + throw new TypeError("Property description must be an object: " + desc); + } + const res: any = {}; + + if ("get" in desc || "set" in desc) { + if ("get" in desc) { + const get = desc.get; + if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get); + res.g = get; + } + if ("set" in desc) { + const set = desc.set; + if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set); + res.s = set; + } + if ("enumerable" in desc) res.e = !!desc.enumerable; + if ("configurable" in desc) res.e = !!desc.configurable; + + if (!object.defineProperty(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key)); + } + else { + if ("enumerable" in desc) res.e = !!desc.enumerable; + if ("configurable" in desc) res.e = !!desc.configurable; + if ("writable" in desc) res.w = !!desc.writable; + if ("value" in desc) res.v = desc.value; + + if (!object.defineField(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key)); + } + + return obj; + } + public static defineProperties(obj: object, desc: PropertyDescriptorMap) { + const keys = object.getOwnMembers(desc, true) as ((keyof typeof obj) & string)[]; + const symbols = object.getOwnSymbolMembers(desc, true) as ((keyof typeof obj) & symbol)[]; + + for (let i = 0; i < keys.length; i++) { + Object.defineProperty(obj, keys[i], desc[keys[i]]); + } + for (let i = 0; i < symbols.length; i++) { + Object.defineProperty(obj, symbols[i], desc[symbols[i]]); + } + + return obj; + } + public static create(proto: object, desc?: PropertyDescriptorMap) { + let res = object.setPrototype({}, proto); + if (desc != null) this.defineProperties(res, desc); + + return res; + } + public static assign(target: any) { + for (let i = 1; i < arguments.length; i++) { + const obj = arguments[i]; + const keys = object.getOwnMembers(obj, false); + const symbols = object.getOwnSymbolMembers(obj, false); + + for (let j = 0; j < keys.length; j++) { + target[keys[j]] = obj[keys[j]]; + } + for (let j = 0; j < symbols.length; j++) { + target[symbols[j]] = obj[symbols[j]]; + } + } + + return target; + } + + public static setPrototypeOf(obj: object, proto: object | null) { + object.setPrototype(obj, proto!); + } + public static getPrototypeOf(obj: object) { + return object.getPrototype(obj) || null; + } + + public static keys(obj: any) { + const res: any[] = []; + const keys = object.getOwnMembers(obj, true); + const symbols = object.getOwnSymbolMembers(obj, true); + + for (let i = 0; i < keys.length; i++) { + res[res.length] = keys[i]; + } + for (let i = 0; i < symbols.length; i++) { + res[res.length] = symbols[i]; + } + + return res; + } + public static values(obj: any) { + const res: any[] = []; + const keys = object.getOwnMembers(obj, true); + const symbols = object.getOwnSymbolMembers(obj, true); + + for (let i = 0; i < keys.length; i++) { + res[res.length] = obj[keys[i]]; + } + for (let i = 0; i < symbols.length; i++) { + res[res.length] = obj[symbols[i]]; + } + + return res; + } + public static entries(obj: any) { + const res: [any, any][] = []; + const keys = object.getOwnMembers(obj, true); + const symbols = object.getOwnSymbolMembers(obj, true); + + for (let i = 0; i < keys.length; i++) { + res[res.length] = [keys[i], obj[keys[i]]]; + } + for (let i = 0; i < symbols.length; i++) { + res[res.length] = [symbols[i], obj[symbols[i]]]; + } + + return res; + } + + public static preventExtensions(obj: object) { + object.preventExt(obj); + return obj; + } + public static seal(obj: object) { + object.seal(obj); + return obj; + } + public static freeze(obj: object) { + object.freeze(obj); + return obj; + } + } + + object.defineProperty(Object.prototype, "__proto__", { e: false }); + object.setPrototype(Object.prototype, undefined); + + func.setCallable(Object, true); + func.setConstructable(Object, true); + + return Object as any as typeof Object & ((value?: unknown) => object); +})(); +export type Object = InstanceType; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java index 65c6c8b..4c3fe1e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java @@ -29,7 +29,7 @@ public class FieldMemberNode implements Member { if (value == null) target.add(Instruction.pushUndefined()); else value.compile(target, true); - target.add(Instruction.defField()); + target.add(Instruction.storeMember()); } public FieldMemberNode(Location loc, Node key, Node value) {