Skip to content

Commit

Permalink
fix(compiler): lifting globals from inflight classes defined in prefl…
Browse files Browse the repository at this point in the history
…ight fails (#5559)

Fixes #2730
The wing compiler always generated dummy preflight classes for inflight classes defined preflight. This means such inflight classes have a lift map and therefore already include all the lift qualification information needed when their mehtods are used inflight. The problem was that our lift visitor ignored references to such classes because it looks for **preflight** expressions to lift, but references to inflight classes aren't preflight expressions.
This PR finds such references and creates a special lift with correct qualifications using dummy singleton preflight object representing the relevant inflight class. To get this working the following changes where made:
* Each inflight class **defined in preflight** gets a unique id
* The generated JS code for each such class stores a map from these unique ids to the generated js preflight class of that inflight class.
* Each generated preflight file (wing module) has this map, and when it's imported by another module its map is merged into the importer's map.
* The root construct contains a global map from all the unique ids to all the inflight types defined preflight. To achieve this I had to make the imported js files local to the construct tree, so they are now imported in the root object's (entry point object) ctor and not globally.
* The base `Resource` class has a satatic `_singleton()` method
* The lift visitor, when encountering a reference to an inflight class instance (defined preflight) will qualify the lift in a similar manner to preflight class instances, but it'll use the `_singleton()` method to get a preflight expression representing that object.

- [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 Jun 27, 2024
1 parent d6d8969 commit fb6404e
Show file tree
Hide file tree
Showing 275 changed files with 2,670 additions and 416 deletions.
1 change: 1 addition & 0 deletions apps/wing/src/commands/pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ describe("wing pack", () => {
expect(mod).toBeDefined();
expect(Object.keys(mod).sort()).toMatchInlineSnapshot(`
[
"$preflightTypesMap",
"FavoriteNumbers",
"Store",
"default",
Expand Down
64 changes: 50 additions & 14 deletions examples/tests/valid/inflight_class_capture_preflight_object.test.w
Original file line number Diff line number Diff line change
@@ -1,18 +1,54 @@
// https://github.com/winglang/wing/issues/2730
// FAILING:
bring cloud;
bring "./subdir2" as subdir;

// bring cloud;
let b = new cloud.Bucket();

// let b = new cloud.Bucket();
// let myConst = "bang bang";
inflight class Foo {
pub uploadToBucket(k: str, value: str) {
b.put(k, value);
assert(b.get(k) == value);
}

// inflight class Foo {
// uploadToBucket(k: str, value: str) {
// b.put(k, value);
// }
// }
static pub fooStatic() {
b.put("a", "b");
assert(b.list() == ["a"]);
}
}

// test "inflight class captures preflight resource" {
// let f = new Foo();
// f.uploadToBucket("hello.txt", "world");
// }
test "inflight class captures preflight resource" {
let f = new Foo();
f.uploadToBucket("hello.txt", "world");
}

test "inflight class type captures preflight resource" {
Foo.fooStatic();
}


let getFoo = inflight () => {
return new Foo();
};

test "inflight class qualified without explicit reference" {
// Get instance of Foo without mentioning the type
let foo = getFoo();
// Now Foo needs to be qualified correcly
foo.uploadToBucket("greetings.txt", "universe");
}

test "inflight class defined inflight captures preflight object" {
class Foo2 {
pub uploadToBucket() {
b.put("x", "y");
assert(b.get("x") == "y");
}
}

let f = new Foo2();
f.uploadToBucket();
}

test "bring inflight class from subdir" {
let x = new subdir.InflightClass();
assert(x.method() == "What did you expect?");
}
5 changes: 5 additions & 0 deletions examples/tests/valid/subdir2/inflight_class.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub inflight class InflightClass {
pub method(): str {
return "What did you expect?";
}
}
13 changes: 9 additions & 4 deletions libs/wingc/src/dtsify/snapshots/declarations.snap
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ const $stdlib = require('@winglang/sdk');
const std = $stdlib.std;
const $helpers = $stdlib.helpers;
const $extern = $helpers.createExternRequire(__dirname);
module.exports = {
...require("./preflight.lib-1.cjs"),
};
const $preflightTypesMap = {};
Object.assign(module.exports, $helpers.bringJs(`${__dirname}/preflight.lib-1.cjs`, $preflightTypesMap));
module.exports = { ...module.exports, $preflightTypesMap };
//# sourceMappingURL=preflight.cjs.map
```
Expand All @@ -125,6 +125,7 @@ const $stdlib = require('@winglang/sdk');
const std = $stdlib.std;
const $helpers = $stdlib.helpers;
const $extern = $helpers.createExternRequire(__dirname);
let $preflightTypesMap = {};
class InflightClass extends $stdlib.std.Resource {
constructor($scope, $id, ) {
super($scope, $id);
Expand Down Expand Up @@ -155,6 +156,8 @@ class InflightClass extends $stdlib.std.Resource {
});
}
}
if ($preflightTypesMap[1]) { throw new Error("InflightClass is already in type map"); }
$preflightTypesMap[1] = InflightClass;
class ParentClass extends $stdlib.std.Resource {
constructor($scope, $id, ) {
super($scope, $id);
Expand Down Expand Up @@ -186,12 +189,14 @@ class ParentClass extends $stdlib.std.Resource {
"bar": [
],
"$inflight_init": [
[InflightClass, []],
],
});
}
static get _liftTypeMap() {
return ({
"static_method": [
[InflightClass, []],
],
});
}
Expand Down Expand Up @@ -225,7 +230,7 @@ class Child extends ParentClass {
});
}
}
module.exports = { InflightClass, ParentClass, Child };
module.exports = { $preflightTypesMap, InflightClass, ParentClass, Child };
//# sourceMappingURL=preflight.lib-1.cjs.map
```
Expand Down
9 changes: 5 additions & 4 deletions libs/wingc/src/dtsify/snapshots/optionals.snap
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ const $stdlib = require('@winglang/sdk');
const std = $stdlib.std;
const $helpers = $stdlib.helpers;
const $extern = $helpers.createExternRequire(__dirname);
module.exports = {
...require("./preflight.lib-1.cjs"),
};
const $preflightTypesMap = {};
Object.assign(module.exports, $helpers.bringJs(`${__dirname}/preflight.lib-1.cjs`, $preflightTypesMap));
module.exports = { ...module.exports, $preflightTypesMap };
//# sourceMappingURL=preflight.cjs.map
```
Expand All @@ -66,6 +66,7 @@ const $stdlib = require('@winglang/sdk');
const std = $stdlib.std;
const $helpers = $stdlib.helpers;
const $extern = $helpers.createExternRequire(__dirname);
let $preflightTypesMap = {};
class ParentClass extends $stdlib.std.Resource {
constructor($scope, $id, ) {
super($scope, $id);
Expand Down Expand Up @@ -96,7 +97,7 @@ class ParentClass extends $stdlib.std.Resource {
});
}
}
module.exports = { ParentClass };
module.exports = { $preflightTypesMap, ParentClass };
//# sourceMappingURL=preflight.lib-1.cjs.map
```
Expand Down
Loading

0 comments on commit fb6404e

Please sign in to comment.