Skip to content

Commit

Permalink
fix ({}).constructor===Object
Browse files Browse the repository at this point in the history
this resolves #899 with a caveat that an object that was created outside
of the VM will obviously won't satisfy this requirement. However, it will
still be `instaceof Object` which is interesting. I am not sure it'll be easy to solve.
  • Loading branch information
Schniz committed Nov 28, 2024
1 parent 3d592c1 commit 7ab7fd3
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 2 deletions.
35 changes: 33 additions & 2 deletions packages/vm/src/edge-vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,25 @@ const transferableConstructors = [
'TypeError',
] as const

function patchInstanceOf(item: string, ctx: any) {
const patchedPrototypes = new Set<(typeof transferableConstructors)[number]>([
'Array',
'Object',
'RegExp',
])

function patchInstanceOf(
item: (typeof transferableConstructors)[number],
ctx: any,
) {
// @ts-ignore
ctx[Symbol.for(`node:${item}`)] = eval(item)

const shouldPatchPrototype = patchedPrototypes.has(item)

return runInContext(
`
globalThis.${item} = new Proxy(${item}, {
(() => {
const proxy = new Proxy(${item}, {
get(target, prop, receiver) {
if (prop === Symbol.hasInstance && receiver === globalThis.${item}) {
const nodeTarget = globalThis[Symbol.for('node:${item}')];
Expand All @@ -137,8 +149,27 @@ function patchInstanceOf(item: string, ctx: any) {
}
return Reflect.get(target, prop, receiver);
},
construct(target, args, newTarget) {
return Object.assign(
Reflect.construct(target, args, newTarget),
{ constructor: proxy }
);
}
})
globalThis.${item} = proxy;
${
!shouldPatchPrototype
? ''
: `Object.assign(globalThis.${item}.prototype, {
get constructor() {
return proxy;
}
})`
}
})()
`,
ctx,
)
Expand Down
29 changes: 29 additions & 0 deletions packages/vm/tests/instanceof.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ it('handles prototype chain correctly', () => {
})
})

describe('.constructor ===', () => {
describe('created inside the vm', () => {
test('new Object().constructor === Object', () => {
const vm = new EdgeVM()
expect(vm.evaluate(`new Object().constructor === Object`)).toBe(true)
})
test('({}).constructor === Object', () => {
const vm = new EdgeVM()
expect(vm.evaluate(`({}).constructor === Object`)).toBe(true)
})
test('new Array().constructor === Array', () => {
const vm = new EdgeVM()
expect(vm.evaluate(`new Array().constructor === Array`)).toBe(true)
})
test('[].constructor === Array', () => {
const vm = new EdgeVM()
expect(vm.evaluate(`[].constructor === Array`)).toBe(true)
})
test('new RegExp("").constructor === Array', () => {
const vm = new EdgeVM()
expect(vm.evaluate(`new RegExp("").constructor === RegExp`)).toBe(true)
})
test('[].constructor === Array', () => {
const vm = new EdgeVM()
expect(vm.evaluate(`/./.constructor === RegExp`)).toBe(true)
})
})
})

describe('instanceof overriding', () => {
test('binary array created outside of the VM is `instanceof` Object inside the VM', () => {
const vm = new EdgeVM()
Expand Down

0 comments on commit 7ab7fd3

Please sign in to comment.