diff --git a/hogvm/__tests__/__snapshots__/operations.hoge b/hogvm/__tests__/__snapshots__/operations.hoge index f11d3fa9ea5b5..3a08b489025d2 100644 --- a/hogvm/__tests__/__snapshots__/operations.hoge +++ b/hogvm/__tests__/__snapshots__/operations.hoge @@ -20,4 +20,10 @@ 35, 29, 2, "toString", 1, 36, 0, 54, 1, 35, 31, 2, "toString", 1, 36, 0, 54, 1, 35, 32, "string", 2, "toString", 1, 36, 0, 54, 1, 35, 32, "1", 2, "toInt", 1, 36, 0, 54, 1, 35, 32, "bla", 2, "toInt", 1, 36, 0, 54, 1, 35, 32, "1.2", 2, "toFloat", 1, 36, 0, 54, 1, 35, 32, "bla", 2, "toFloat", 1, 36, 0, 54, 1, 35, 32, "asd", 2, "toUUID", 1, 36, 0, 54, 1, -35, 31, 33, 1, 11, 36, 0, 54, 1, 35, 31, 33, 1, 12, 36, 0, 54, 1, 35, 35] +35, 31, 33, 1, 11, 36, 0, 54, 1, 35, 31, 33, 1, 12, 36, 0, 54, 1, 35, 33, 1, 32, "1", 11, 36, 0, 54, 1, 35, 32, "1", 33, +1, 11, 36, 0, 54, 1, 35, 29, 33, 1, 11, 36, 0, 54, 1, 35, 29, 33, 0, 11, 36, 0, 54, 1, 35, 29, 33, 2, 11, 36, 0, 54, 1, +35, 30, 33, 1, 12, 36, 0, 54, 1, 35, 32, "2", 33, 1, 11, 36, 0, 54, 1, 35, 32, "2", 33, 1, 11, 36, 0, 54, 1, 35, 32, +"2", 33, 1, 12, 36, 0, 54, 1, 35, 32, "2", 33, 1, 15, 36, 0, 54, 1, 35, 32, "2", 33, 1, 16, 36, 0, 54, 1, 35, 32, "2", +33, 1, 13, 36, 0, 54, 1, 35, 32, "2", 33, 1, 14, 36, 0, 54, 1, 35, 33, 2, 32, "1", 11, 36, 0, 54, 1, 35, 33, 2, 32, "1", +11, 36, 0, 54, 1, 35, 33, 2, 32, "1", 12, 36, 0, 54, 1, 35, 33, 2, 32, "1", 15, 36, 0, 54, 1, 35, 33, 2, 32, "1", 16, +36, 0, 54, 1, 35, 33, 2, 32, "1", 13, 36, 0, 54, 1, 35, 33, 2, 32, "1", 14, 36, 0, 54, 1, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/operations.stdout b/hogvm/__tests__/__snapshots__/operations.stdout index 5017b0e3005c7..849bb2699265f 100644 --- a/hogvm/__tests__/__snapshots__/operations.stdout +++ b/hogvm/__tests__/__snapshots__/operations.stdout @@ -60,3 +60,23 @@ null "asd" false true +true +true +true +false +false +true +false +false +true +true +true +false +false +false +false +true +true +true +false +false diff --git a/hogvm/__tests__/operations.hog b/hogvm/__tests__/operations.hog index 9f65924bed192..259353afcf798 100644 --- a/hogvm/__tests__/operations.hog +++ b/hogvm/__tests__/operations.hog @@ -64,3 +64,23 @@ test(toFloat('bla')) // null test(toUUID('asd')) // 'asd' test(1 == null) // false test(1 != null) // true +test('1' = 1) // true +test(1 = '1') // true +test(1 == true) // true +test(0 == true) // false +test(2 == true) // false +test(1 != false) // true +test(1 = '2') // false +test(1 == '2') // false +test(1 != '2') // true +test(1 < '2') // true +test(1 <= '2') // true +test(1 > '2') // false +test(1 >= '2') // false +test('1' = 2) // false +test('1' == 2) // false +test('1' != 2) // true +test('1' < 2) // true +test('1' <= 2) // true +test('1' > 2) // false +test('1' >= 2) // false diff --git a/hogvm/python/execute.py b/hogvm/python/execute.py index a8553628c73b2..2d11cae58d611 100644 --- a/hogvm/python/execute.py +++ b/hogvm/python/execute.py @@ -19,6 +19,7 @@ like, set_nested_value, calculate_cost, + unify_comparison_types, ) if TYPE_CHECKING: @@ -201,17 +202,23 @@ def capture_upvalue(index) -> dict: case Operation.MOD: push_stack(pop_stack() % pop_stack()) case Operation.EQ: - push_stack(pop_stack() == pop_stack()) + var1, var2 = unify_comparison_types(pop_stack(), pop_stack()) + push_stack(var1 == var2) case Operation.NOT_EQ: - push_stack(pop_stack() != pop_stack()) + var1, var2 = unify_comparison_types(pop_stack(), pop_stack()) + push_stack(var1 != var2) case Operation.GT: - push_stack(pop_stack() > pop_stack()) + var1, var2 = unify_comparison_types(pop_stack(), pop_stack()) + push_stack(var1 > var2) case Operation.GT_EQ: - push_stack(pop_stack() >= pop_stack()) + var1, var2 = unify_comparison_types(pop_stack(), pop_stack()) + push_stack(var1 >= var2) case Operation.LT: - push_stack(pop_stack() < pop_stack()) + var1, var2 = unify_comparison_types(pop_stack(), pop_stack()) + push_stack(var1 < var2) case Operation.LT_EQ: - push_stack(pop_stack() <= pop_stack()) + var1, var2 = unify_comparison_types(pop_stack(), pop_stack()) + push_stack(var1 <= var2) case Operation.LIKE: push_stack(like(pop_stack(), pop_stack())) case Operation.ILIKE: diff --git a/hogvm/python/utils.py b/hogvm/python/utils.py index ebb18ca751ec2..4a8e899266233 100644 --- a/hogvm/python/utils.py +++ b/hogvm/python/utils.py @@ -90,3 +90,19 @@ def calculate_cost(object, marked: set | None = None) -> int: elif isinstance(object, str): return COST_PER_UNIT + len(object) return COST_PER_UNIT + + +def unify_comparison_types(left, right): + if isinstance(left, int | float) and isinstance(right, str): + return left, float(right) + if isinstance(left, str) and isinstance(right, int | float): + return float(left), right + if isinstance(left, bool) and isinstance(right, str): + return left, bool(right) + if isinstance(left, str) and isinstance(right, bool): + return bool(left), right + if isinstance(left, int | float) and isinstance(right, bool): + return left, int(right) + if isinstance(left, bool) and isinstance(right, int | float): + return int(left), right + return left, right diff --git a/hogvm/typescript/package.json b/hogvm/typescript/package.json index 31cb1a812ec31..8a52d73906343 100644 --- a/hogvm/typescript/package.json +++ b/hogvm/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@posthog/hogvm", - "version": "1.0.43", + "version": "1.0.44", "description": "PostHog Hog Virtual Machine", "types": "dist/index.d.ts", "source": "src/index.ts", diff --git a/hogvm/typescript/src/execute.ts b/hogvm/typescript/src/execute.ts index 5ef6edd259bf3..090de4076a54a 100644 --- a/hogvm/typescript/src/execute.ts +++ b/hogvm/typescript/src/execute.ts @@ -13,6 +13,7 @@ import { like, setNestedValue, UncaughtHogVMException, + unifyComparisonTypes, } from './utils' export function execSync(bytecode: any[], options?: ExecOptions): any { @@ -280,22 +281,28 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { pushStack(Number(popStack()) % Number(popStack())) break case Operation.EQ: - pushStack(popStack() === popStack()) + ;[temp, temp2] = unifyComparisonTypes(popStack(), popStack()) + pushStack(temp === temp2) break case Operation.NOT_EQ: - pushStack(popStack() !== popStack()) + ;[temp, temp2] = unifyComparisonTypes(popStack(), popStack()) + pushStack(temp !== temp2) break case Operation.GT: - pushStack(popStack() > popStack()) + ;[temp, temp2] = unifyComparisonTypes(popStack(), popStack()) + pushStack(temp > temp2) break case Operation.GT_EQ: - pushStack(popStack() >= popStack()) + ;[temp, temp2] = unifyComparisonTypes(popStack(), popStack()) + pushStack(temp >= temp2) break case Operation.LT: - pushStack(popStack() < popStack()) + ;[temp, temp2] = unifyComparisonTypes(popStack(), popStack()) + pushStack(temp < temp2) break case Operation.LT_EQ: - pushStack(popStack() <= popStack()) + ;[temp, temp2] = unifyComparisonTypes(popStack(), popStack()) + pushStack(temp <= temp2) break case Operation.LIKE: pushStack(like(popStack(), popStack())) diff --git a/hogvm/typescript/src/utils.ts b/hogvm/typescript/src/utils.ts index 45ea53bfedb83..077b7ea451c89 100644 --- a/hogvm/typescript/src/utils.ts +++ b/hogvm/typescript/src/utils.ts @@ -164,3 +164,25 @@ export function calculateCost(object: any, marked: Set | undefined = undefi } return COST_PER_UNIT } + +export function unifyComparisonTypes(left: any, right: any): [any, any] { + if (typeof left === 'number' && typeof right === 'string') { + return [left, Number(right)] + } + if (typeof left === 'string' && typeof right === 'number') { + return [Number(left), right] + } + if (typeof left === 'boolean' && typeof right === 'string') { + return [left, right === 'true'] + } + if (typeof left === 'string' && typeof right === 'boolean') { + return [left === 'true', right] + } + if (typeof left === 'boolean' && typeof right === 'number') { + return [left ? 1 : 0, right] + } + if (typeof left === 'number' && typeof right === 'boolean') { + return [left, right ? 1 : 0] + } + return [left, right] +} diff --git a/package.json b/package.json index 10c345338e744..8a67dc54ba8c3 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@medv/finder": "^3.1.0", "@microlink/react-json-view": "^1.21.3", "@monaco-editor/react": "4.6.0", - "@posthog/hogvm": "^1.0.43", + "@posthog/hogvm": "^1.0.44", "@posthog/icons": "0.7.3", "@posthog/plugin-scaffold": "^1.4.4", "@react-hook/size": "^2.1.2", diff --git a/plugin-server/package.json b/plugin-server/package.json index e78a741b1f1d1..75a137b2e1924 100644 --- a/plugin-server/package.json +++ b/plugin-server/package.json @@ -53,7 +53,7 @@ "@maxmind/geoip2-node": "^3.4.0", "@posthog/clickhouse": "^1.7.0", "@posthog/cyclotron": "file:../rust/cyclotron-node", - "@posthog/hogvm": "^1.0.43", + "@posthog/hogvm": "^1.0.44", "@posthog/plugin-scaffold": "1.4.4", "@sentry/node": "^7.49.0", "@sentry/profiling-node": "^0.3.0", diff --git a/plugin-server/pnpm-lock.yaml b/plugin-server/pnpm-lock.yaml index d3709d54436d3..08f08046ad14c 100644 --- a/plugin-server/pnpm-lock.yaml +++ b/plugin-server/pnpm-lock.yaml @@ -47,8 +47,8 @@ dependencies: specifier: file:../rust/cyclotron-node version: file:../rust/cyclotron-node '@posthog/hogvm': - specifier: ^1.0.43 - version: 1.0.43(luxon@3.4.4) + specifier: ^1.0.44 + version: 1.0.44(luxon@3.4.4) '@posthog/plugin-scaffold': specifier: 1.4.4 version: 1.4.4 @@ -3116,8 +3116,8 @@ packages: engines: {node: '>=12'} dev: false - /@posthog/hogvm@1.0.43(luxon@3.4.4): - resolution: {integrity: sha512-VjZ7uwxlWB5XoZNoH/xhxv3SVikvrcMuTV+LjV7j6Ddw1pFDvWlebMHNLgtrUZKChrs+ZhOOE6KWk5qPDv48bg==} + /@posthog/hogvm@1.0.44(luxon@3.4.4): + resolution: {integrity: sha512-Ss7gTPyvPyviNipVQOqnsCa66IMmMf+DEg7iX/vQMcWDuFwvHNbdze1iwFVoXCjLci+h8SW2rOMPB0S5A2jJXg==} peerDependencies: luxon: ^3.4.4 dependencies: diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36714e0990d5a..833dec3dea675 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,8 +53,8 @@ dependencies: specifier: 4.6.0 version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.2.0)(react@18.2.0) '@posthog/hogvm': - specifier: ^1.0.43 - version: 1.0.43(luxon@3.5.0) + specifier: ^1.0.44 + version: 1.0.44(luxon@3.5.0) '@posthog/icons': specifier: 0.7.3 version: 0.7.3(react-dom@18.2.0)(react@18.2.0) @@ -374,7 +374,7 @@ dependencies: optionalDependencies: fsevents: specifier: ^2.3.2 - version: 2.3.3 + version: 2.3.2 devDependencies: '@babel/core': @@ -5414,8 +5414,8 @@ packages: resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} dev: false - /@posthog/hogvm@1.0.43(luxon@3.5.0): - resolution: {integrity: sha512-VjZ7uwxlWB5XoZNoH/xhxv3SVikvrcMuTV+LjV7j6Ddw1pFDvWlebMHNLgtrUZKChrs+ZhOOE6KWk5qPDv48bg==} + /@posthog/hogvm@1.0.44(luxon@3.5.0): + resolution: {integrity: sha512-Ss7gTPyvPyviNipVQOqnsCa66IMmMf+DEg7iX/vQMcWDuFwvHNbdze1iwFVoXCjLci+h8SW2rOMPB0S5A2jJXg==} peerDependencies: luxon: ^3.4.4 dependencies: @@ -13077,7 +13077,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] requiresBuild: true - dev: true optional: true /fsevents@2.3.3: