diff --git a/docs/annotations.md b/docs/annotations.md index 8ae916254..ef33dab06 100644 --- a/docs/annotations.md +++ b/docs/annotations.md @@ -48,10 +48,11 @@ end transpiles to ```brightscript +sub __MyComp_method_new() +end sub function __MyComp_builder() instance = {} - instance.new = sub() - end sub + instance.new = __MyComp_method_new return instance end function function MyComp() diff --git a/docs/classes.md b/docs/classes.md index 90b0980ad..c157f0d36 100644 --- a/docs/classes.md +++ b/docs/classes.md @@ -19,13 +19,15 @@ end class And here's the transpiled BrightScript code ```BrightScript +sub __Animal_method_new() + m.name = invalid +end sub +function __Animal_method_walk() +end function function __Animal_builder() instance = {} - instance.new = sub() - m.name = invalid - end sub - instance.walk = function() - end function + instance.new = __Animal_method_new + instance.walk = __Animal_method_walk return instance end function function Animal() @@ -35,7 +37,9 @@ function Animal() end function ``` -Notice that there are two functions created in the transpiled code for the `Animal` class. At runtime, BrighterScript classes are built in two steps in order to support class inheritance. The first step uses the `__ClassName_Build()` method to create the skeleton structure of the class. Then the class's constructor function will be run. Child classes will call the parent's `__ParentClassName_Build()` method, then rename overridden methods, and then call the child's constructor (without calling the parent constructor). Take a look at the transpiled output of the other examples below for more information on this. +Notice that there are four functions created in the transpiled code for the `Animal` class: +- At runtime, BrighterScript classes are built in two steps in order to support class inheritance. The first step uses the `__ClassName_builder()` method to create the skeleton structure of the class. Then the class's constructor function will be run. Child classes will call the parent's `__ParentClassName_builder()` method, then rewrite overridden methods, and then call the child's constructor (without calling the parent constructor). Take a look at the transpiled output of the other examples below for more information on this. +- Class methods are hoisted as named functions, to prevent an excessive amount of "$anon_..." functions in stack traces, crash logs, and profiler outputs. ## Inheritance @@ -85,15 +89,17 @@ end sub View the transpiled BrightScript code ```BrightScript +sub __Animal_method_new(name as string) + m.name = invalid + m.name = name +end sub +sub __Animal_method_move(distanceInMeters as integer) + print m.name + " moved " + distanceInMeters.ToStr() + " meters" +end sub function __Animal_builder() instance = {} - instance.new = sub(name as string) - m.name = invalid - m.name = name - end sub - instance.move = sub(distanceInMeters as integer) - print m.name + " moved " + distanceInMeters.ToStr() + " meters" - end sub + instance.new = __Animal_method_new + instance.move = __Animal_method_move return instance end function function Animal(name as string) @@ -101,17 +107,19 @@ function Animal(name as string) instance.new(name) return instance end function +sub __Duck_method_new(name as string) + m.super0_new(name) +end sub +sub __Duck_method_move(distanceInMeters as integer) + print "Waddling..." + m.super0_move(distanceInMeters) +end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub(name as string) - m.super0_new(name) - end sub + instance.new = __Duck_method_new instance.super0_move = instance.move - instance.move = sub(distanceInMeters as integer) - print "Waddling..." - m.super0_move(distanceInMeters) - end sub + instance.move = __Duck_method_move return instance end function function Duck(name as string) @@ -119,17 +127,19 @@ function Duck(name as string) instance.new(name) return instance end function +sub __BabyDuck_method_new(name as string) + m.super1_new(name) +end sub +sub __BabyDuck_method_move(distanceInMeters as integer) + m.super1_move(distanceInMeters) + print "Fell over...I'm new at this" +end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super1_new = instance.new - instance.new = sub(name as string) - m.super1_new(name) - end sub + instance.new = __BabyDuck_method_new instance.super1_move = instance.move - instance.move = sub(distanceInMeters as integer) - m.super1_move(distanceInMeters) - print "Fell over...I'm new at this" - end sub + instance.move = __BabyDuck_method_move return instance end function function BabyDuck(name as string) @@ -170,13 +180,14 @@ end sub View the transpiled BrightScript code ```BrightScript +sub __Duck_method_new(name as string) + m.name = invalid + m.end sub = invalid + m.name = name +end sub function __Duck_builder() instance = {} - instance.new = sub(name as string) - m.name = invalid - m.end sub = invalid - m.name = name - end sub + instance.new = __Duck_method_new return instance end function function Duck(name as string) @@ -212,12 +223,13 @@ end class View the transpiled BrightScript code ```BrightScript +sub __Duck_method_move(name as string) + m.name = invalid + m.name = name +end sub function __Duck_builder() instance = {} - instance.new = sub(name as string) - m.name = invalid - m.name = name - end sub + instance.new = __Duck_method_move return instance end function function Duck(name as string) @@ -225,16 +237,17 @@ function Duck(name as string) instance.new(name) return instance end function +sub __BabyDuck_method_move(name as string, age as integer) + m.super0_new() + m.age = invalid + 'the first line in this constructor must be a call to super() + m.super0_new(name) + m.age = age +end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super0_new = instance.new - instance.new = sub(name as string, age as integer) - m.super0_new() - m.age = invalid - 'the first line in this constructor must be a call to super() - m.super0_new(name) - m.age = age - end sub + instance.new = __BabyDuck_method_move return instance end function function BabyDuck(name as string, age as integer) @@ -267,13 +280,15 @@ end sub View the transpiled BrightScript code ```BrightScript +sub __Duck_method_new() +end sub +sub __Duck_method_Eat() + print "Ate all the food" +end sub function __Duck_builder() instance = {} - instance.new = sub() - end sub - instance.Eat = sub() - print "Ate all the food" - end sub + instance.new = __Duck_method_new + instance.Eat = __Duck_method_Eat return instance end function function Duck() @@ -281,15 +296,16 @@ function Duck() instance.new() return instance end function +sub __BabyDuck_method_new() +end sub +sub __BabyDuck_method_Eat() + print "Ate half the food, because I'm a baby duck" +end sub function __BabyDuck_builder() instance = {} - instance.new = sub() - m.end sub = invalid - end sub - instance.super-1_Eat = instance.Eat - instance.Eat = sub() - print "Ate half the food, because I'm a baby duck" - end sub + instance.new = __BabyDuck_method_new + instance.super1_Eat = instance.Eat + instance.Eat = __BabyDuck_method_Eat return instance end function function BabyDuck() @@ -322,13 +338,15 @@ end class View the transpiled BrightScript code ```BrightScript +sub __Duck_method_new() +end sub +sub __Duck_method_walk(meters as integer) + print "Walked " + meters.ToStr() + " meters" +end sub function __Duck_builder() instance = {} - instance.new = sub() - end sub - instance.walk = sub(meters as integer) - print "Walked " + meters.ToStr() + " meters" - end sub + instance.new = __Duck_method_new + instance.walk = __Duck_method_walk return instance end function function Duck() @@ -336,17 +354,19 @@ function Duck() instance.new() return instance end function +sub __BabyDuck_method_new() + m.super0_new() +end sub +sub __BabyDuck_method_walk(meters as integer) + print "Tripped" + m.super0_walk(meters) +end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __BabyDuck_method_new instance.super0_walk = instance.walk - instance.walk = sub(meters as integer) - print "Tripped" - m.super0_walk(meters) - end sub + instance.walk = __BabyDuck_method_walk return instance end function function BabyDuck() @@ -383,22 +403,25 @@ end class View the transpiled BrightScript code ```BrightScript +sub __Person_method_new() + m.name = invalid + m.socialSecurityNumber = invalid +end sub +function __Person_method_getName() + return m.name +end function +sub __Person_method_setSocialSecurityNumber(value as string) + m.socialSecurityNumber = value +end sub function __Person_builder() instance = {} - instance.new = sub() - m.name = invalid - m.socialSecurityNumber = invalid - end sub + instance.new = __Person_method_new 'defaults to public 'specified private 'defaults to public - instance.getName = function() - return m.name - end function + instance.getName = __Person_method_getName 'specified private - instance.setSocialSecurityNumber = sub(value as string) - m.socialSecurityNumber = value - end sub + instance.setSocialSecurityNumber = __Person_method_setSocialSecurityNumber return instance end function function Person() @@ -414,7 +437,7 @@ You will get compile-time errors whenever you access private members of a class ## Dynamic type by default You can specify a type for class fields and a return type for methods. However, this is entirely optional. All fields and methods have a default type of `dynamic`. However, BrighterScript will attempt to infer the type from usage. Take this for example: -```vb +```BrighterScript class Person 'defaults to type "dynamic" name @@ -432,18 +455,20 @@ end class View the transpiled BrightScript code ```BrightScript +sub __Person_method_new() + m.name = invalid + m.age = 12 +end sub +function __Person_method_getAge() + return m.age +end function function __Person_builder() instance = {} - instance.new = sub() - m.name = invalid - m.age = 12 - end sub + instance.new = __Person_method_new 'defaults to type "dynamic" 'infers type "integer" 'infers type "integer" - instance.getAge = function() - return m.age - end function + instance.getAge = __Person_method_getAge return instance end function function Person() @@ -467,12 +492,13 @@ end class View the transpiled BrightScript code ```BrightScript +sub __Duck_method_new() + m.name = "Donald" + m.hasChildren = true +end sub function __Duck_builder() instance = {} - instance.new = sub() - m.name = "Donald" - m.hasChildren = true - end sub + instance.new = __Duck_method_new return instance end function function Duck() @@ -517,10 +543,11 @@ end namespace View the transpiled BrightScript code ```BrightScript +sub __Vertibrates_Birds_Animal_method_new() +end sub function __Vertibrates_Birds_Animal_builder() instance = {} - instance.new = sub() - end sub + instance.new = __Vertibrates_Birds_Animal_method_new return instance end function function Vertibrates_Birds_Animal() @@ -528,12 +555,13 @@ function Vertibrates_Birds_Animal() instance.new() return instance end function +sub __Vertibrates_Birds_Duck_method_new() + m.super0_new() +end sub function __Vertibrates_Birds_Duck_builder() instance = __Vertibrates_Birds_Animal_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __Vertibrates_Birds_Duck_method_new return instance end function function Vertibrates_Birds_Duck() @@ -580,14 +608,16 @@ end sub View the transpiled BrightScript code ```BrightScript +sub __Person_method_new(name as string) + m.name = name +end sub +sub __Person_method_sayHello() + print m.name +end sub function __Person_builder() instance = {} - instance.new = sub(name as string) - m.name = name - end sub - instance.sayHello = sub() - print m.name - end sub + instance.new = __Person_method_new + instance.sayHello = __Person_method_sayHello return instance end function function Person(name as string) @@ -628,15 +658,16 @@ end class View the transpiled BrightScript code ```BrightScript +sub __Video_method_new() + m.url = invalid + m.length = invalid + m.subtitleUrl = invalid + m.rating = invalid + m.genre = invalid +end sub function __Video_builder() instance = {} - instance.new = sub() - m.url = invalid - m.length = invalid - m.subtitleUrl = invalid - m.rating = invalid - m.genre = invalid - end sub + instance.new = __Video_method_new return instance end function function Video() diff --git a/src/files/BrsFile.Class.spec.ts b/src/files/BrsFile.Class.spec.ts index 6ad265412..d0ae89065 100644 --- a/src/files/BrsFile.Class.spec.ts +++ b/src/files/BrsFile.Class.spec.ts @@ -251,12 +251,13 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Animal_method_new() + m.species1 = "Animal" + print "From Animal: " + m.species + end sub function __Animal_builder() instance = {} - instance.new = sub() - m.species1 = "Animal" - print "From Animal: " + m.species - end sub + instance.new = __Animal_method_new return instance end function function Animal() @@ -264,14 +265,15 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Duck_method_new() + m.super0_new() + m.species2 = "Duck" + print "From Duck: " + m.species + end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - m.species2 = "Duck" - print "From Duck: " + m.species - end sub + instance.new = __Duck_method_new return instance end function function Duck() @@ -293,10 +295,11 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Animal_method_new() + end sub function __Animal_builder() instance = {} - instance.new = sub() - end sub + instance.new = __Animal_method_new return instance end function function Animal() @@ -304,13 +307,14 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Duck_method_new() + 'comment should not cause double super call + m.super0_new() + end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub() - 'comment should not cause double super call - m.super0_new() - end sub + instance.new = __Duck_method_new return instance end function function Duck() @@ -332,10 +336,11 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Animal_method_new() + end sub function __Animal_builder() instance = {} - instance.new = sub() - end sub + instance.new = __Animal_method_new return instance end function function Animal() @@ -343,13 +348,14 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Duck_method_new() + print "I am a statement which does not use m" + m.super0_new() + end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub() - print "I am a statement which does not use m" - m.super0_new() - end sub + instance.new = __Duck_method_new return instance end function function Duck() @@ -372,11 +378,12 @@ describe('BrsFile BrighterScript classes', () => { className3 = "BabyDuck" end class `, ` + sub __Animal_method_new() + m.className1 = "Animal" + end sub function __Animal_builder() instance = {} - instance.new = sub() - m.className1 = "Animal" - end sub + instance.new = __Animal_method_new return instance end function function Animal() @@ -384,13 +391,14 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Duck_method_new() + m.super0_new() + m.className2 = "Duck" + end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - m.className2 = "Duck" - end sub + instance.new = __Duck_method_new return instance end function function Duck() @@ -398,13 +406,14 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __BabyDuck_method_new() + m.super1_new() + m.className3 = "BabyDuck" + end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super1_new = instance.new - instance.new = sub() - m.super1_new() - m.className3 = "BabyDuck" - end sub + instance.new = __BabyDuck_method_new return instance end function function BabyDuck() @@ -424,10 +433,11 @@ describe('BrsFile BrighterScript classes', () => { end class end namespace `, ` + sub __Birds_WaterFowl_Duck_method_new() + end sub function __Birds_WaterFowl_Duck_builder() instance = {} - instance.new = sub() - end sub + instance.new = __Birds_WaterFowl_Duck_method_new return instance end function function Birds_WaterFowl_Duck() @@ -435,12 +445,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Birds_WaterFowl_BabyDuck_method_new() + m.super0_new() + end sub function __Birds_WaterFowl_BabyDuck_builder() instance = __Birds_WaterFowl_Duck_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __Birds_WaterFowl_BabyDuck_method_new return instance end function function Birds_WaterFowl_BabyDuck() @@ -456,10 +467,11 @@ describe('BrsFile BrighterScript classes', () => { class Duck end class `, ` + sub __Duck_method_new() + end sub function __Duck_builder() instance = {} - instance.new = sub() - end sub + instance.new = __Duck_method_new return instance end function function Duck() @@ -487,10 +499,11 @@ describe('BrsFile BrighterScript classes', () => { class BabyDuck extends Duck end class `, ` + sub __Animal_method_new(p1) + end sub function __Animal_builder() instance = {} - instance.new = sub(p1) - end sub + instance.new = __Animal_method_new return instance end function function Animal(p1) @@ -498,12 +511,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new(p1) return instance end function + sub __Bird_method_new(p1) + m.super0_new(p1) + end sub function __Bird_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub(p1) - m.super0_new(p1) - end sub + instance.new = __Bird_method_new return instance end function function Bird(p1) @@ -511,13 +525,14 @@ describe('BrsFile BrighterScript classes', () => { instance.new(p1) return instance end function + sub __Duck_method_new(p1, p2) + m.super1_new(p1) + m.p2 = p2 + end sub function __Duck_builder() instance = __Bird_builder() instance.super1_new = instance.new - instance.new = sub(p1, p2) - m.super1_new(p1) - m.p2 = p2 - end sub + instance.new = __Duck_method_new return instance end function function Duck(p1, p2) @@ -525,12 +540,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new(p1, p2) return instance end function + sub __BabyDuck_method_new(p1, p2) + m.super2_new(p1, p2) + end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super2_new = instance.new - instance.new = sub(p1, p2) - m.super2_new(p1, p2) - end sub + instance.new = __BabyDuck_method_new return instance end function function BabyDuck(p1, p2) @@ -548,10 +564,11 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Duck_method_new(name as string, age as integer) + end sub function __Duck_builder() instance = {} - instance.new = sub(name as string, age as integer) - end sub + instance.new = __Duck_method_new return instance end function function Duck(name as string, age as integer) @@ -576,10 +593,11 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Animal_method_new(name as string) + end sub function __Animal_builder() instance = {} - instance.new = sub(name as string) - end sub + instance.new = __Animal_method_new return instance end function function Animal(name as string) @@ -587,13 +605,14 @@ describe('BrsFile BrighterScript classes', () => { instance.new(name) return instance end function + sub __Duck_method_new(name as string, age as integer) + m.super0_new(name) + m.super0_DoSomething() + end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub(name as string, age as integer) - m.super0_new(name) - m.super0_DoSomething() - end sub + instance.new = __Duck_method_new return instance end function function Duck(name as string, age as integer) @@ -623,13 +642,15 @@ describe('BrsFile BrighterScript classes', () => { end function end class `, ` + sub __Creature_method_new(name as string) + end sub + function __Creature_method_sayHello(text) + ? text + end function function __Creature_builder() instance = {} - instance.new = sub(name as string) - end sub - instance.sayHello = function(text) - ? text - end function + instance.new = __Creature_method_new + instance.sayHello = __Creature_method_sayHello return instance end function function Creature(name as string) @@ -637,19 +658,21 @@ describe('BrsFile BrighterScript classes', () => { instance.new(name) return instance end function + sub __Duck_method_new(name as string) + m.super0_new(name) + end sub + function __Duck_method_sayHello(text) + text = "The duck says " + text + if text <> invalid + m.super0_sayHello(text) + end if + end function function __Duck_builder() instance = __Creature_builder() instance.super0_new = instance.new - instance.new = sub(name as string) - m.super0_new(name) - end sub + instance.new = __Duck_method_new instance.super0_sayHello = instance.sayHello - instance.sayHello = function(text) - text = "The duck says " + text - if text <> invalid - m.super0_sayHello(text) - end if - end function + instance.sayHello = __Duck_method_sayHello return instance end function function Duck(name as string) @@ -728,10 +751,11 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Parent_method_new() + end sub function __Parent_builder() instance = {} - instance.new = sub() - end sub + instance.new = __Parent_method_new return instance end function function Parent() @@ -739,12 +763,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Child_method_new() + m.super0_new() + end sub function __Child_builder() instance = __Parent_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __Child_method_new return instance end function function Child() @@ -766,11 +791,12 @@ describe('BrsFile BrighterScript classes', () => { name = "Bob" end class `, ` + sub __Person_method_new() + m.name = "Bob" + end sub function __Person_builder() instance = {} - instance.new = sub() - m.name = "Bob" - end sub + instance.new = __Person_method_new return instance end function function Person() @@ -826,15 +852,17 @@ describe('BrsFile BrighterScript classes', () => { '> Waddling...\\nDewey moved 2 meters\\nFell over...I'm new at this end sub `, ` + sub __Animal_method_new(name as string) + m.name = invalid + m.name = name + end sub + sub __Animal_method_move(distanceInMeters as integer) + print m.name + " moved " + distanceInMeters.ToStr() + " meters" + end sub function __Animal_builder() instance = {} - instance.new = sub(name as string) - m.name = invalid - m.name = name - end sub - instance.move = sub(distanceInMeters as integer) - print m.name + " moved " + distanceInMeters.ToStr() + " meters" - end sub + instance.new = __Animal_method_new + instance.move = __Animal_method_move return instance end function function Animal(name as string) @@ -842,17 +870,19 @@ describe('BrsFile BrighterScript classes', () => { instance.new(name) return instance end function + sub __Duck_method_new(name as string) + m.super0_new(name) + end sub + sub __Duck_method_move(distanceInMeters as integer) + print "Waddling..." + m.super0_move(distanceInMeters) + end sub function __Duck_builder() instance = __Animal_builder() instance.super0_new = instance.new - instance.new = sub(name as string) - m.super0_new(name) - end sub + instance.new = __Duck_method_new instance.super0_move = instance.move - instance.move = sub(distanceInMeters as integer) - print "Waddling..." - m.super0_move(distanceInMeters) - end sub + instance.move = __Duck_method_move return instance end function function Duck(name as string) @@ -860,17 +890,19 @@ describe('BrsFile BrighterScript classes', () => { instance.new(name) return instance end function + sub __BabyDuck_method_new(name as string) + m.super1_new(name) + end sub + sub __BabyDuck_method_move(distanceInMeters as integer) + m.super1_move(distanceInMeters) + print "Fell over...I'm new at this" + end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super1_new = instance.new - instance.new = sub(name as string) - m.super1_new(name) - end sub + instance.new = __BabyDuck_method_new instance.super1_move = instance.move - instance.move = sub(distanceInMeters as integer) - m.super1_move(distanceInMeters) - print "Fell over...I'm new at this" - end sub + instance.move = __BabyDuck_method_move return instance end function function BabyDuck(name as string) @@ -908,13 +940,15 @@ describe('BrsFile BrighterScript classes', () => { end sub end class `, ` + sub __Duck_method_new() + end sub + sub __Duck_method_walk(meters as integer) + print "Walked " + meters.ToStr() + " meters" + end sub function __Duck_builder() instance = {} - instance.new = sub() - end sub - instance.walk = sub(meters as integer) - print "Walked " + meters.ToStr() + " meters" - end sub + instance.new = __Duck_method_new + instance.walk = __Duck_method_walk return instance end function function Duck() @@ -922,17 +956,19 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __BabyDuck_method_new() + m.super0_new() + end sub + sub __BabyDuck_method_walk(meters as integer) + print "Tripped" + m.super0_walk(meters) + end sub function __BabyDuck_builder() instance = __Duck_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __BabyDuck_method_new instance.super0_walk = instance.walk - instance.walk = sub(meters as integer) - print "Tripped" - m.super0_walk(meters) - end sub + instance.walk = __BabyDuck_method_walk return instance end function function BabyDuck() @@ -955,11 +991,12 @@ describe('BrsFile BrighterScript classes', () => { end enum end namespace `, ` + sub __MyNS_HasEnumKlass_method_new() + m.enumValue = "A" + end sub function __MyNS_HasEnumKlass_builder() instance = {} - instance.new = sub() - m.enumValue = "A" - end sub + instance.new = __MyNS_HasEnumKlass_method_new return instance end function function MyNS_HasEnumKlass() @@ -989,12 +1026,13 @@ describe('BrsFile BrighterScript classes', () => { end enum end namespace `, ` + sub __MyNS_SubKlass_method_new() + m.super0_new("B") + end sub function __MyNS_SubKlass_builder() instance = __MyNS_SuperKlass_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new("B") - end sub + instance.new = __MyNS_SubKlass_method_new return instance end function function MyNS_SubKlass() @@ -1002,11 +1040,12 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __MyNS_SuperKlass_method_new(enumVal) + print enumVal + end sub function __MyNS_SuperKlass_builder() instance = {} - instance.new = sub(enumVal) - print enumVal - end sub + instance.new = __MyNS_SuperKlass_method_new return instance end function function MyNS_SuperKlass(enumVal) @@ -1034,16 +1073,17 @@ describe('BrsFile BrighterScript classes', () => { end enum end namespace `, ` + sub __MyNS_HasEnumKlass_method_new() + m.myArray = [ + true + true + ] + m.myArray[0] = true + m.myArray[1] = false + end sub function __MyNS_HasEnumKlass_builder() instance = {} - instance.new = sub() - m.myArray = [ - true - true - ] - m.myArray[0] = true - m.myArray[1] = false - end sub + instance.new = __MyNS_HasEnumKlass_method_new return instance end function function MyNS_HasEnumKlass() @@ -1070,16 +1110,17 @@ describe('BrsFile BrighterScript classes', () => { end enum end namespace `, ` + sub __MyNS_HasEnumKlass_method_new() + m.myArray = [ + true + true + ] + m.myArray[0] = true + m.myArray[1] = false + end sub function __MyNS_HasEnumKlass_builder() instance = {} - instance.new = sub() - m.myArray = [ - true - true - ] - m.myArray[0] = true - m.myArray[1] = false - end sub + instance.new = __MyNS_HasEnumKlass_method_new return instance end function function MyNS_HasEnumKlass() @@ -1197,13 +1238,15 @@ describe('BrsFile BrighterScript classes', () => { expect( fsExtra.readFileSync(s`${stagingDir}/source/lib.brs`).toString().trimEnd() ).to.eql(trim` + sub __Being_method_new() + end sub + function __Being_method_think() + print "thinking..." + end function function __Being_builder() instance = {} - instance.new = sub() - end sub - instance.think = function() - print "thinking..." - end function + instance.new = __Being_method_new + instance.think = __Being_method_think return instance end function function Being() @@ -1211,15 +1254,17 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __Human_method_new() + m.super0_new() + end sub + function __Human_method_think() + m.super0_think() + end function function __Human_builder() instance = __Being_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub - instance.think = function() - m.super0_think() - end function + instance.new = __Human_method_new + instance.think = __Human_method_think return instance end function function Human() @@ -1687,12 +1732,13 @@ describe('BrsFile BrighterScript classes', () => { end class end namespace `, ` + sub __App_ClassC_method_new() + m.super1_new() + end sub function __App_ClassC_builder() instance = __App_ClassB_builder() instance.super1_new = instance.new - instance.new = sub() - m.super1_new() - end sub + instance.new = __App_ClassC_method_new return instance end function function App_ClassC() @@ -1715,12 +1761,13 @@ describe('BrsFile BrighterScript classes', () => { end class end namespace `, ` + sub __App_ClassB_method_new() + m.super0_new() + end sub function __App_ClassB_builder() instance = __ClassA_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __App_ClassB_method_new return instance end function function App_ClassB() @@ -1787,11 +1834,12 @@ describe('BrsFile BrighterScript classes', () => { end class end namespace `, ` + sub __App_CoreClass_method_new() + print "CoreClass.new()" + end sub function __App_CoreClass_builder() instance = {} - instance.new = sub() - print "CoreClass.new()" - end sub + instance.new = __App_CoreClass_method_new return instance end function function App_CoreClass() @@ -1799,12 +1847,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __App_Logic_FirstClass_method_new() + m.super0_new() + end sub function __App_Logic_FirstClass_builder() instance = __App_CoreClass_builder() instance.super0_new = instance.new - instance.new = sub() - m.super0_new() - end sub + instance.new = __App_Logic_FirstClass_method_new return instance end function function App_Logic_FirstClass() @@ -1812,12 +1861,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __App_Logic_SecondClass_method_new() + m.super1_new() + end sub function __App_Logic_SecondClass_builder() instance = __App_Logic_FirstClass_builder() instance.super1_new = instance.new - instance.new = sub() - m.super1_new() - end sub + instance.new = __App_Logic_SecondClass_method_new return instance end function function App_Logic_SecondClass() @@ -1825,12 +1875,13 @@ describe('BrsFile BrighterScript classes', () => { instance.new() return instance end function + sub __App_OtherLogic_FinalClass_method_new() + m.super2_new() + end sub function __App_OtherLogic_FinalClass_builder() instance = __App_Logic_SecondClass_builder() instance.super2_new = instance.new - instance.new = sub() - m.super2_new() - end sub + instance.new = __App_OtherLogic_FinalClass_method_new return instance end function function App_OtherLogic_FinalClass() diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index 24b613729..dfac05a94 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -2387,14 +2387,16 @@ describe('BrsFile', () => { ' alpha.charlie() end sub + sub __Person_method_new() + m.name = invalid + print m.name + end sub + sub __Person_method_test() + end sub function __Person_builder() instance = {} - instance.new = sub() - m.name = invalid - print m.name - end sub - instance.test = sub() - end sub + instance.new = __Person_method_new + instance.test = __Person_method_test return instance end function function Person() diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts index d3fc7b46e..ea8fee117 100644 --- a/src/parser/Statement.ts +++ b/src/parser/Statement.ts @@ -2036,14 +2036,19 @@ export class ClassStatement extends Statement implements TypedefProvider { transpile(state: BrsTranspileState) { let result = [] as TranspileResult; + + const className = this.getName(ParseMode.BrightScript).replace(/\./g, '_'); + const ancestors = this.getAncestors(state); + const body = this.getTranspiledClassBody(ancestors); + + //make the methods + result.push(...this.getTranspiledMethods(state, className, body)); //make the builder - result.push(...this.getTranspiledBuilder(state)); - result.push( - '\n', - state.indent() - ); + result.push(...this.getTranspiledBuilder(state, className, ancestors, body)); + result.push('\n', state.indent()); //make the class assembler (i.e. the public-facing class creator method) - result.push(...this.getTranspiledClassFunction(state)); + result.push(...this.getTranspiledClassFunction(state, className)); + return result; } @@ -2158,11 +2163,12 @@ export class ClassStatement extends Statement implements TypedefProvider { return ancestors; } - private getBuilderName(name: string) { - if (name.includes('.')) { - name = name.replace(/\./gi, '_'); - } - return `__${name}_builder`; + private getBuilderName(transpiledClassName: string) { + return `__${transpiledClassName}_builder`; + } + + private getMethodIdentifier(transpiledClassName: string, statement: MethodStatement) { + return { ...statement.name, text: `__${transpiledClassName}_method_${statement.name.text}` }; } /** @@ -2207,18 +2213,13 @@ export class ClassStatement extends Statement implements TypedefProvider { * This needs to be a separate function so that child classes can call the builder from their parent * without instantiating the parent constructor at that point in time. */ - private getTranspiledBuilder(state: BrsTranspileState) { + private getTranspiledBuilder(state: BrsTranspileState, transpiledClassName: string, ancestors: ClassStatement[], body: Statement[]) { let result = [] as TranspileResult; - result.push(`function ${this.getBuilderName(this.getName(ParseMode.BrightScript)!)}()\n`); + result.push(`function ${this.getBuilderName(transpiledClassName)}()\n`); state.blockDepth++; //indent result.push(state.indent()); - /** - * The lineage of this class. index 0 is a direct parent, index 1 is index 0's parent, etc... - */ - let ancestors = this.getAncestors(state); - //construct parent class or empty object if (ancestors[0]) { const ancestorNamespace = ancestors[0].findAncestor(isNamespaceStatement); @@ -2226,9 +2227,7 @@ export class ClassStatement extends Statement implements TypedefProvider { ancestors[0].getName(ParseMode.BrighterScript)!, ancestorNamespace?.getName(ParseMode.BrighterScript) ); - result.push( - 'instance = ', - this.getBuilderName(fullyQualifiedClassName), '()'); + result.push(`instance = ${this.getBuilderName(fullyQualifiedClassName.replace(/\./g, '_'))}()`); } else { //use an empty object. result.push('instance = {}'); @@ -2239,43 +2238,6 @@ export class ClassStatement extends Statement implements TypedefProvider { ); let parentClassIndex = this.getParentClassIndex(state); - let body = this.body; - //inject an empty "new" method if missing - if (!this.getConstructorFunction()) { - if (ancestors.length === 0) { - body = [ - createMethodStatement('new', TokenKind.Sub), - ...this.body - ]; - } else { - const params = this.getConstructorParams(ancestors); - const call = new ExpressionStatement( - new CallExpression( - new VariableExpression(createToken(TokenKind.Identifier, 'super')), - createToken(TokenKind.LeftParen), - createToken(TokenKind.RightParen), - params.map(x => new VariableExpression(x.name)) - ) - ); - body = [ - new MethodStatement( - [], - createIdentifier('new'), - new FunctionExpression( - params.map(x => x.clone()), - new Block([call]), - createToken(TokenKind.Sub), - createToken(TokenKind.EndSub), - createToken(TokenKind.LeftParen), - createToken(TokenKind.RightParen) - ), - null - ), - ...this.body - ]; - } - } - for (let statement of body) { //is field statement if (isFieldStatement(statement)) { @@ -2304,7 +2266,7 @@ export class ClassStatement extends Statement implements TypedefProvider { 'instance.', state.transpileToken(statement.name), ' = ', - ...statement.transpile(state), + state.transpileToken(this.getMethodIdentifier(transpiledClassName, statement)), state.newline, state.indent() ); @@ -2326,12 +2288,74 @@ export class ClassStatement extends Statement implements TypedefProvider { return result; } + /** + * Returns a copy of the class' body, with the constructor function added if it doesn't exist. + */ + private getTranspiledClassBody(ancestors: ClassStatement[]): Statement[] { + const body = []; + body.push(...this.body); + + //inject an empty "new" method if missing + if (!this.getConstructorFunction()) { + if (ancestors.length === 0) { + body.unshift(createMethodStatement('new', TokenKind.Sub)); + } else { + const params = this.getConstructorParams(ancestors); + const call = new ExpressionStatement( + new CallExpression( + new VariableExpression(createToken(TokenKind.Identifier, 'super')), + createToken(TokenKind.LeftParen), + createToken(TokenKind.RightParen), + params.map(x => new VariableExpression(x.name)) + ) + ); + body.unshift( + new MethodStatement( + [], + createIdentifier('new'), + new FunctionExpression( + params.map(x => x.clone()), + new Block([call]), + createToken(TokenKind.Sub), + createToken(TokenKind.EndSub), + createToken(TokenKind.LeftParen), + createToken(TokenKind.RightParen) + ), + null + ) + ); + } + } + + return body; + } + + /** + * These are the methods that are defined in this class. They are transpiled outside of the class body + * to ensure they don't appear as "$anon_#" in stack traces and crash logs. + */ + private getTranspiledMethods(state: BrsTranspileState, transpiledClassName: string, body: Statement[]) { + let result = [] as TranspileResult; + for (let statement of body) { + if (isMethodStatement(statement)) { + state.classStatement = this; + result.push( + ...statement.transpile(state, this.getMethodIdentifier(transpiledClassName, statement)), + state.newline, + state.indent() + ); + delete state.classStatement; + } + } + return result; + } + /** * The class function is the function with the same name as the class. This is the function that * consumers should call to create a new instance of that class. * This invokes the builder, gets an instance of the class, then invokes the "new" function on that class. */ - private getTranspiledClassFunction(state: BrsTranspileState) { + private getTranspiledClassFunction(state: BrsTranspileState, transpiledClassName: string) { let result = [] as TranspileResult; const constructorFunction = this.getConstructorFunction(); @@ -2365,7 +2389,7 @@ export class ClassStatement extends Statement implements TypedefProvider { state.blockDepth++; result.push(state.indent()); - result.push(`instance = ${this.getBuilderName(this.getName(ParseMode.BrightScript)!)}()\n`); + result.push(`instance = ${this.getBuilderName(transpiledClassName)}()\n`); result.push(state.indent()); result.push(`instance.new(`); @@ -2461,7 +2485,7 @@ export class MethodStatement extends FunctionStatement { return this.name.text; } - transpile(state: BrsTranspileState) { + transpile(state: BrsTranspileState, name?: Identifier) { if (this.name.text.toLowerCase() === 'new') { this.ensureSuperConstructorCall(state); //TODO we need to undo this at the bottom of this method @@ -2490,7 +2514,7 @@ export class MethodStatement extends FunctionStatement { visitor(statement, undefined); statement.walk(visitor, walkOptions); } - return this.func.transpile(state); + return this.func.transpile(state, name); } getTypedef(state: BrsTranspileState) {