Skip to content

Commit

Permalink
fix(compiler): methods on tokenized strings used inflight use lifted …
Browse files Browse the repository at this point in the history
…non evaluated string (#5766)

See #4292.
We now special case access to phase independent properties of string objects. Such properties will get the phase of the execution environment so when used inflight we'll use the evaluated tokenized string.

## Checklist

- [x] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [x] Description explains motivation and solution
- [x] Tests added (always)
- [ ] Docs updated (only required for features)
- [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
yoav-steinberg authored Feb 25, 2024
1 parent f5b5e3e commit 745df1e
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 48 deletions.
22 changes: 22 additions & 0 deletions examples/tests/valid/phase_independent_method_on_string.test.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
bring cloud;
bring regex;

let api = new cloud.Api();
let url_regex = "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]\{1,256\}\\.[a-zA-Z0-9()]\{1,6\}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)";

// Verify the url is not a valid url (because it's a token)
assert(!regex.match(url_regex, api.url));

let token_len = api.url.length;


test "phase independent method on string evaluated inflight" {
// Make sure api.url isn't a token but evaled inflight
assert(regex.match(url_regex, api.url));

// Call a method on the url (should be called inflight)
assert(api.url.startsWith("http"));

// Call the length property (should be called inflight) and make sure it's different than the preflight length
assert(api.url.length != token_len);
}
10 changes: 5 additions & 5 deletions libs/wingc/src/jsify/snapshots/access_property_on_primitive.snap
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ source: libs/wingc/src/jsify/tests.rs
```js
"use strict";
const $helpers = require("@winglang/sdk/lib/helpers");
module.exports = function({ $s_length }) {
module.exports = function({ $s }) {
class $Closure1 {
constructor({ }) {
const $obj = (...args) => this.handle(...args);
Object.setPrototypeOf($obj, this);
return $obj;
}
async handle() {
$helpers.assert($helpers.eq($s_length, 5), "s.length == 5");
$helpers.assert($helpers.eq($s.length, 5), "s.length == 5");
}
}
return $Closure1;
Expand Down Expand Up @@ -56,7 +56,7 @@ class $Root extends $stdlib.std.Resource {
static _toInflightType() {
return `
require("${$helpers.normalPath(__dirname)}/inflight.$Closure1-1.js")({
$s_length: ${$stdlib.core.liftObject(s.length)},
$s: ${$stdlib.core.liftObject(s)},
})
`;
}
Expand All @@ -74,10 +74,10 @@ class $Root extends $stdlib.std.Resource {
get _liftMap() {
return ({
"handle": [
[s.length, []],
[s, ["length"]],
],
"$inflight_init": [
[s.length, []],
[s, []],
],
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ source: libs/wingc/src/jsify/tests.rs
```js
"use strict";
const $helpers = require("@winglang/sdk/lib/helpers");
module.exports = function({ $__obj__key_______if____key_in_obj___throw_new_Error__Map_does_not_contain_key_____key______return_obj_key______s___hello___length }) {
module.exports = function({ $__obj__key_______if____key_in_obj___throw_new_Error__Map_does_not_contain_key_____key______return_obj_key______s___hello__ }) {
class $Closure1 {
constructor({ }) {
const $obj = (...args) => this.handle(...args);
Object.setPrototypeOf($obj, this);
return $obj;
}
async handle() {
$helpers.assert($helpers.eq($__obj__key_______if____key_in_obj___throw_new_Error__Map_does_not_contain_key_____key______return_obj_key______s___hello___length, 3), "s.get(\"hello\").length == 3");
$helpers.assert($helpers.eq($__obj__key_______if____key_in_obj___throw_new_Error__Map_does_not_contain_key_____key______return_obj_key______s___hello__.length, 3), "s.get(\"hello\").length == 3");
}
}
return $Closure1;
Expand Down Expand Up @@ -56,7 +56,7 @@ class $Root extends $stdlib.std.Resource {
static _toInflightType() {
return `
require("${$helpers.normalPath(__dirname)}/inflight.$Closure1-1.js")({
$__obj__key_______if____key_in_obj___throw_new_Error__Map_does_not_contain_key_____key______return_obj_key______s___hello___length: ${$stdlib.core.liftObject(((obj, key) => { if (!(key in obj)) throw new Error(`Map does not contain key: "${key}"`); return obj[key]; })(s, "hello").length)},
$__obj__key_______if____key_in_obj___throw_new_Error__Map_does_not_contain_key_____key______return_obj_key______s___hello__: ${$stdlib.core.liftObject(((obj, key) => { if (!(key in obj)) throw new Error(`Map does not contain key: "${key}"`); return obj[key]; })(s, "hello"))},
})
`;
}
Expand All @@ -74,10 +74,10 @@ class $Root extends $stdlib.std.Resource {
get _liftMap() {
return ({
"handle": [
[((obj, key) => { if (!(key in obj)) throw new Error(`Map does not contain key: "${key}"`); return obj[key]; })(s, "hello").length, []],
[((obj, key) => { if (!(key in obj)) throw new Error(`Map does not contain key: "${key}"`); return obj[key]; })(s, "hello"), ["length"]],
],
"$inflight_init": [
[((obj, key) => { if (!(key in obj)) throw new Error(`Map does not contain key: "${key}"`); return obj[key]; })(s, "hello").length, []],
[((obj, key) => { if (!(key in obj)) throw new Error(`Map does not contain key: "${key}"`); return obj[key]; })(s, "hello"), []],
],
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ source: libs/wingc/src/jsify/tests.rs
```js
"use strict";
const $helpers = require("@winglang/sdk/lib/helpers");
module.exports = function({ $x_length }) {
module.exports = function({ $x }) {
class $Closure1 {
constructor({ }) {
const $obj = (...args) => this.handle(...args);
Object.setPrototypeOf($obj, this);
return $obj;
}
async handle() {
$helpers.assert(($x_length > 0), "x.length > 0");
$helpers.assert(($x.length > 0), "x.length > 0");
}
}
return $Closure1;
Expand Down Expand Up @@ -55,7 +55,7 @@ class $Root extends $stdlib.std.Resource {
static _toInflightType() {
return `
require("${$helpers.normalPath(__dirname)}/inflight.$Closure1-1.js")({
$x_length: ${$stdlib.core.liftObject(x.length)},
$x: ${$stdlib.core.liftObject(x)},
})
`;
}
Expand All @@ -73,10 +73,10 @@ class $Root extends $stdlib.std.Resource {
get _liftMap() {
return ({
"handle": [
[x.length, []],
[x, ["length"]],
],
"$inflight_init": [
[x.length, []],
[x, []],
],
});
}
Expand Down
9 changes: 3 additions & 6 deletions libs/wingc/src/jsify/snapshots/preflight_value_field.snap
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ source: libs/wingc/src/jsify/tests.rs
```js
"use strict";
const $helpers = require("@winglang/sdk/lib/helpers");
module.exports = function({ $t_last, $t_name, $t_name_length }) {
module.exports = function({ $t_last, $t_name }) {
class $Closure1 {
constructor({ }) {
const $obj = (...args) => this.handle(...args);
Expand All @@ -39,7 +39,7 @@ module.exports = function({ $t_last, $t_name, $t_name_length }) {
}
async handle() {
console.log($t_name);
$helpers.assert(($t_name_length > 0), "t.name.length > 0");
$helpers.assert(($t_name.length > 0), "t.name.length > 0");
console.log($t_last);
}
}
Expand Down Expand Up @@ -117,7 +117,6 @@ class $Root extends $stdlib.std.Resource {
require("${$helpers.normalPath(__dirname)}/inflight.$Closure1-1.js")({
$t_last: ${$stdlib.core.liftObject(t.last)},
$t_name: ${$stdlib.core.liftObject(t.name)},
$t_name_length: ${$stdlib.core.liftObject(t.name.length)},
})
`;
}
Expand All @@ -136,13 +135,11 @@ class $Root extends $stdlib.std.Resource {
return ({
"handle": [
[t.last, []],
[t.name, []],
[t.name.length, []],
[t.name, ["length"]],
],
"$inflight_init": [
[t.last, []],
[t.name, []],
[t.name.length, []],
],
});
}
Expand Down
12 changes: 6 additions & 6 deletions libs/wingc/src/jsify/snapshots/reference_preflight_fields.snap
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ source: libs/wingc/src/jsify/tests.rs
const $helpers = require("@winglang/sdk/lib/helpers");
module.exports = function({ }) {
class MyType {
constructor({ $this_b, $this_s_length }) {
constructor({ $this_b, $this_s }) {
this.$this_b = $this_b;
this.$this_s_length = $this_s_length;
this.$this_s = $this_s;
}
async boom() {
$helpers.assert((this.$this_s_length > 0), "this.s.length > 0");
$helpers.assert((this.$this_s.length > 0), "this.s.length > 0");
$helpers.assert(((await this.$this_b.list()).length > 0), "this.b.list().length > 0");
}
async bam() {
Expand Down Expand Up @@ -86,7 +86,7 @@ class $Root extends $stdlib.std.Resource {
const MyTypeClient = ${MyType._toInflightType()};
const client = new MyTypeClient({
$this_b: ${$stdlib.core.liftObject(this.b)},
$this_s_length: ${$stdlib.core.liftObject(this.s.length)},
$this_s: ${$stdlib.core.liftObject(this.s)},
});
if (client.$inflight_init) { await client.$inflight_init(); }
return client;
Expand All @@ -97,14 +97,14 @@ class $Root extends $stdlib.std.Resource {
return ({
"boom": [
[this.b, ["list"]],
[this.s.length, []],
[this.s, ["length"]],
],
"bam": [
[this.b, ["get", "put"]],
],
"$inflight_init": [
[this.b, []],
[this.s.length, []],
[this.s, []],
],
});
}
Expand Down
10 changes: 8 additions & 2 deletions libs/wingc/src/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5481,9 +5481,15 @@ impl<'a> TypeChecker<'a> {

// Try to resolve phase independent property's actual phase
property_phase = if property_phase == Phase::Independent {
// If the object is a string we treat the phase independent property based on our env.
// Strings might be tokens and not evaluated yet. Tokenized strings accessed inflight
// must be evaluated in inflight.
if instance_type.is_string() {
env.phase
}
// When the property is phase independent and either the object phase is inflight or we're
// passing inflight args to the method call, then we need treat the property as inflight too
if instance_phase == Phase::Inflight || callee_with_inflight_args {
// passing inflight args to the method call, then we need treat the property as inflight too.
else if instance_phase == Phase::Inflight || callee_with_inflight_args {
Phase::Inflight
} else {
// Default to instance phase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
const $helpers = require("@winglang/sdk/lib/helpers");
module.exports = function({ }) {
class R {
constructor({ $_this_s1_concat___world___ }) {
this.$_this_s1_concat___world___ = $_this_s1_concat___world___;
constructor({ $this_s1 }) {
this.$this_s1 = $this_s1;
}
async foo() {
console.log(this.$_this_s1_concat___world___);
console.log((await this.$this_s1.concat(" world")));
}
}
return R;
Expand Down Expand Up @@ -66,7 +66,7 @@ class $Root extends $stdlib.std.Resource {
(await (async () => {
const RClient = ${R._toInflightType()};
const client = new RClient({
$_this_s1_concat___world___: ${$stdlib.core.liftObject((this.s1.concat(" world")))},
$this_s1: ${$stdlib.core.liftObject(this.s1)},
});
if (client.$inflight_init) { await client.$inflight_init(); }
return client;
Expand All @@ -76,10 +76,10 @@ class $Root extends $stdlib.std.Resource {
get _liftMap() {
return ({
"foo": [
[(this.s1.concat(" world")), []],
[this.s1, ["concat"]],
],
"$inflight_init": [
[(this.s1.concat(" world")), []],
[this.s1, []],
],
});
}
Expand Down
Loading

0 comments on commit 745df1e

Please sign in to comment.