EngineTests.cs 115 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102
  1. using System.Globalization;
  2. using System.Reflection;
  3. using Jint.Native;
  4. using Jint.Native.Array;
  5. using Jint.Native.Number;
  6. using Jint.Native.Object;
  7. using Jint.Runtime;
  8. using Jint.Runtime.Debugger;
  9. using Jint.Tests.Runtime.Debugger;
  10. using Xunit.Abstractions;
  11. #pragma warning disable 618
  12. namespace Jint.Tests.Runtime
  13. {
  14. public partial class EngineTests : IDisposable
  15. {
  16. private readonly Engine _engine;
  17. private int countBreak = 0;
  18. private StepMode stepMode;
  19. private static readonly TimeZoneInfo _pacificTimeZone;
  20. private static readonly TimeZoneInfo _tongaTimeZone;
  21. private static readonly TimeZoneInfo _easternTimeZone;
  22. static EngineTests()
  23. {
  24. // https://stackoverflow.com/questions/47848111/how-should-i-fetch-timezoneinfo-in-a-platform-agnostic-way
  25. // should be natively supported soon https://github.com/dotnet/runtime/issues/18644
  26. try
  27. {
  28. _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Los_Angeles");
  29. }
  30. catch (TimeZoneNotFoundException)
  31. {
  32. _pacificTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
  33. }
  34. try
  35. {
  36. _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific/Tongatapu");
  37. }
  38. catch (TimeZoneNotFoundException)
  39. {
  40. _tongaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Tonga Standard Time");
  41. }
  42. try
  43. {
  44. _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
  45. }
  46. catch (TimeZoneNotFoundException)
  47. {
  48. _easternTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US Eastern Standard Time");
  49. }
  50. }
  51. public EngineTests(ITestOutputHelper output)
  52. {
  53. _engine = new Engine()
  54. .SetValue("log", new Action<object>(o => output.WriteLine(o.ToString())))
  55. .SetValue("assert", new Action<bool>(Assert.True))
  56. .SetValue("equal", new Action<object, object>(Assert.Equal))
  57. ;
  58. }
  59. void IDisposable.Dispose()
  60. {
  61. }
  62. private void RunTest(string source)
  63. {
  64. _engine.Execute(source);
  65. }
  66. internal static string GetEmbeddedFile(string filename)
  67. {
  68. const string Prefix = "Jint.Tests.Runtime.Scripts.";
  69. var assembly = typeof(EngineTests).GetTypeInfo().Assembly;
  70. var scriptPath = Prefix + filename;
  71. using var stream = assembly.GetManifestResourceStream(scriptPath);
  72. using var sr = new StreamReader(stream);
  73. return sr.ReadToEnd();
  74. }
  75. [Theory]
  76. [InlineData(42d, "42")]
  77. [InlineData("Hello", "'Hello'")]
  78. public void ShouldInterpretLiterals(object expected, string source)
  79. {
  80. var engine = new Engine();
  81. var result = engine.Evaluate(source).ToObject();
  82. Assert.Equal(expected, result);
  83. }
  84. [Fact]
  85. public void ShouldInterpretVariableDeclaration()
  86. {
  87. var engine = new Engine();
  88. var result = engine
  89. .Evaluate("var foo = 'bar'; foo;")
  90. .ToObject();
  91. Assert.Equal("bar", result);
  92. }
  93. [Theory]
  94. [InlineData(4d, "1 + 3")]
  95. [InlineData(-2d, "1 - 3")]
  96. [InlineData(3d, "1 * 3")]
  97. [InlineData(2d, "6 / 3")]
  98. [InlineData(9d, "15 & 9")]
  99. [InlineData(15d, "15 | 9")]
  100. [InlineData(6d, "15 ^ 9")]
  101. [InlineData(36d, "9 << 2")]
  102. [InlineData(2d, "9 >> 2")]
  103. [InlineData(4d, "19 >>> 2")]
  104. public void ShouldInterpretBinaryExpression(object expected, string source)
  105. {
  106. var engine = new Engine();
  107. var result = engine.Evaluate(source).ToObject();
  108. Assert.Equal(expected, result);
  109. }
  110. [Theory]
  111. [InlineData(-59d, "~58")]
  112. [InlineData(58d, "~~58")]
  113. public void ShouldInterpretUnaryExpression(object expected, string source)
  114. {
  115. var engine = new Engine();
  116. var result = engine.Evaluate(source).ToObject();
  117. Assert.Equal(expected, result);
  118. }
  119. [Fact]
  120. public void ShouldHaveProperReferenceErrorMessage()
  121. {
  122. RunTest(@"
  123. 'use strict';
  124. var arr = [1, 2];
  125. try {
  126. for (i in arr) { }
  127. assert(false);
  128. }
  129. catch (ex) {
  130. assert(ex.message === 'i is not defined');
  131. }
  132. ");
  133. }
  134. [Fact]
  135. public void ShouldHaveProperNotAFunctionErrorMessage()
  136. {
  137. RunTest(@"
  138. try {
  139. var example = {};
  140. example();
  141. assert(false);
  142. }
  143. catch (ex) {
  144. assert(ex.message === 'example is not a function');
  145. }
  146. ");
  147. }
  148. [Fact]
  149. public void ShouldEvaluateHasOwnProperty()
  150. {
  151. RunTest(@"
  152. var x = {};
  153. x.Bar = 42;
  154. assert(x.hasOwnProperty('Bar'));
  155. ");
  156. }
  157. [Fact]
  158. public void ShouldAllowNullAsStringValue()
  159. {
  160. var engine = new Engine().SetValue("name", (string) null);
  161. Assert.True(engine.Evaluate("name").IsNull());
  162. }
  163. [Fact]
  164. public void FunctionConstructorsShouldCreateNewObjects()
  165. {
  166. RunTest(@"
  167. var Vehicle = function () {};
  168. var vehicle = new Vehicle();
  169. assert(vehicle != undefined);
  170. ");
  171. }
  172. [Fact]
  173. public void NewObjectsInheritFunctionConstructorProperties()
  174. {
  175. RunTest(@"
  176. var Vehicle = function () {};
  177. var vehicle = new Vehicle();
  178. Vehicle.prototype.wheelCount = 4;
  179. assert(vehicle.wheelCount == 4);
  180. assert((new Vehicle()).wheelCount == 4);
  181. ");
  182. }
  183. [Fact]
  184. public void PrototypeFunctionIsInherited()
  185. {
  186. RunTest(@"
  187. function Body(mass){
  188. this.mass = mass;
  189. }
  190. Body.prototype.offsetMass = function(dm) {
  191. this.mass += dm;
  192. return this;
  193. }
  194. var b = new Body(36);
  195. b.offsetMass(6);
  196. assert(b.mass == 42);
  197. ");
  198. }
  199. [Fact]
  200. public void FunctionConstructorCall()
  201. {
  202. RunTest(@"
  203. function Body(mass){
  204. this.mass = mass;
  205. }
  206. var john = new Body(36);
  207. assert(john.mass == 36);
  208. ");
  209. }
  210. [Fact]
  211. public void ArrowFunctionCall()
  212. {
  213. RunTest(@"
  214. var add = (a, b) => {
  215. return a + b;
  216. }
  217. var x = add(1, 2);
  218. assert(x == 3);
  219. ");
  220. }
  221. [Fact]
  222. public void ArrowFunctionExpressionCall()
  223. {
  224. RunTest(@"
  225. var add = (a, b) => a + b;
  226. var x = add(1, 2);
  227. assert(x === 3);
  228. ");
  229. }
  230. [Fact]
  231. public void ArrowFunctionScope()
  232. {
  233. RunTest(@"
  234. var bob = {
  235. _name: ""Bob"",
  236. _friends: [""Alice""],
  237. printFriends() {
  238. this._friends.forEach(f => assert(this._name === ""Bob"" && f === ""Alice""))
  239. }
  240. };
  241. bob.printFriends();
  242. ");
  243. }
  244. [Fact]
  245. public void NewObjectsShouldUsePrivateProperties()
  246. {
  247. RunTest(@"
  248. var Vehicle = function (color) {
  249. this.color = color;
  250. };
  251. var vehicle = new Vehicle('tan');
  252. assert(vehicle.color == 'tan');
  253. ");
  254. }
  255. [Fact]
  256. public void FunctionConstructorsShouldDefinePrototypeChain()
  257. {
  258. RunTest(@"
  259. function Vehicle() {};
  260. var vehicle = new Vehicle();
  261. assert(vehicle.hasOwnProperty('constructor') == false);
  262. ");
  263. }
  264. [Fact]
  265. public void NewObjectsConstructorIsObject()
  266. {
  267. RunTest(@"
  268. var o = new Object();
  269. assert(o.constructor == Object);
  270. ");
  271. }
  272. [Fact]
  273. public void NewObjectsIntanceOfConstructorObject()
  274. {
  275. RunTest(@"
  276. var o = new Object();
  277. assert(o instanceof Object);
  278. ");
  279. }
  280. [Fact]
  281. public void NewObjectsConstructorShouldBeConstructorObject()
  282. {
  283. RunTest(@"
  284. var Vehicle = function () {};
  285. var vehicle = new Vehicle();
  286. assert(vehicle.constructor == Vehicle);
  287. ");
  288. }
  289. [Fact]
  290. public void NewObjectsIntanceOfConstructorFunction()
  291. {
  292. RunTest(@"
  293. var Vehicle = function () {};
  294. var vehicle = new Vehicle();
  295. assert(vehicle instanceof Vehicle);
  296. ");
  297. }
  298. [Fact]
  299. public void ShouldEvaluateForLoops()
  300. {
  301. RunTest(@"
  302. var foo = 0;
  303. for (var i = 0; i < 5; i++) {
  304. foo += i;
  305. }
  306. assert(foo == 10);
  307. ");
  308. }
  309. [Fact]
  310. public void ShouldEvaluateRecursiveFunctions()
  311. {
  312. RunTest(@"
  313. function fib(n) {
  314. if (n < 2) {
  315. return n;
  316. }
  317. return fib(n - 1) + fib(n - 2);
  318. }
  319. var result = fib(6);
  320. assert(result == 8);
  321. ");
  322. }
  323. [Fact]
  324. public void ShouldAccessObjectProperties()
  325. {
  326. RunTest(@"
  327. var o = {};
  328. o.Foo = 'bar';
  329. o.Baz = 42;
  330. o.Blah = o.Foo + o.Baz;
  331. assert(o.Blah == 'bar42');
  332. ");
  333. }
  334. [Fact]
  335. public void ShouldConstructArray()
  336. {
  337. RunTest(@"
  338. var o = [];
  339. assert(o.length == 0);
  340. ");
  341. }
  342. [Fact]
  343. public void ArrayPushShouldIncrementLength()
  344. {
  345. RunTest(@"
  346. var o = [];
  347. o.push(1);
  348. assert(o.length == 1);
  349. ");
  350. }
  351. [Fact]
  352. public void ArrayFunctionInitializesLength()
  353. {
  354. RunTest(@"
  355. assert(Array(3).length == 3);
  356. assert(Array('3').length == 1);
  357. ");
  358. }
  359. [Fact]
  360. public void ArrayIndexerIsAssigned()
  361. {
  362. RunTest(@"
  363. var n = 8;
  364. var o = Array(n);
  365. for (var i = 0; i < n; i++) o[i] = i;
  366. equal(0, o[0]);
  367. equal(7, o[7]);
  368. ");
  369. }
  370. [Fact]
  371. public void DenseArrayTurnsToSparseArrayWhenSizeGrowsTooMuch()
  372. {
  373. RunTest(@"
  374. var n = 1024*10+2;
  375. var o = Array(n);
  376. for (var i = 0; i < n; i++) o[i] = i;
  377. equal(0, o[0]);
  378. equal(n -1, o[n - 1]);
  379. ");
  380. }
  381. [Fact]
  382. public void DenseArrayTurnsToSparseArrayWhenSparseIndexed()
  383. {
  384. RunTest(@"
  385. var o = Array();
  386. o[100] = 1;
  387. assert(o[100] == 1);
  388. ");
  389. }
  390. [Fact]
  391. public void ArrayPopShouldDecrementLength()
  392. {
  393. RunTest(@"
  394. var o = [42, 'foo'];
  395. var pop = o.pop();
  396. assert(o.length == 1);
  397. assert(pop == 'foo');
  398. ");
  399. }
  400. [Fact]
  401. public void ArrayConstructor()
  402. {
  403. RunTest(@"
  404. var o = [];
  405. assert(o.constructor == Array);
  406. ");
  407. }
  408. [Fact]
  409. public void DateConstructor()
  410. {
  411. RunTest(@"
  412. var o = new Date();
  413. assert(o.constructor == Date);
  414. assert(o.hasOwnProperty('constructor') == false);
  415. ");
  416. }
  417. [Fact]
  418. public void DateConstructorWithInvalidParameters()
  419. {
  420. RunTest(@"
  421. var dt = new Date (1, Infinity);
  422. assert(isNaN(dt.getTime()));
  423. ");
  424. }
  425. [Fact]
  426. public void ShouldConvertDateToNumber()
  427. {
  428. RunTest(@"
  429. assert(Number(new Date(0)) === 0);
  430. ");
  431. }
  432. [Fact]
  433. public void MathObjectIsDefined()
  434. {
  435. RunTest(@"
  436. var o = Math.abs(-1)
  437. assert(o == 1);
  438. ");
  439. }
  440. [Fact]
  441. public void VoidShouldReturnUndefined()
  442. {
  443. RunTest(@"
  444. assert(void 0 === undefined);
  445. var x = '1';
  446. assert(void x === undefined);
  447. x = 'x';
  448. assert (isNaN(void x) === true);
  449. x = new String('-1');
  450. assert (void x === undefined);
  451. ");
  452. }
  453. [Fact]
  454. public void TypeofObjectShouldReturnString()
  455. {
  456. RunTest(@"
  457. assert(typeof x === 'undefined');
  458. assert(typeof 0 === 'number');
  459. var x = 0;
  460. assert (typeof x === 'number');
  461. var x = new Object();
  462. assert (typeof x === 'object');
  463. ");
  464. }
  465. [Fact]
  466. public void MathAbsReturnsAbsolute()
  467. {
  468. RunTest(@"
  469. assert(1 == Math.abs(-1));
  470. ");
  471. }
  472. [Fact]
  473. public void NaNIsNan()
  474. {
  475. RunTest(@"
  476. var x = NaN;
  477. assert(isNaN(NaN));
  478. assert(isNaN(Math.abs(x)));
  479. ");
  480. }
  481. [Theory]
  482. [InlineData(2147483647, 1, 2147483648)]
  483. [InlineData(-2147483647, -2, -2147483649)]
  484. public void IntegerAdditionShouldNotOverflow(int lhs, int rhs, long result)
  485. {
  486. RunTest($"assert({lhs} + {rhs} == {result})");
  487. }
  488. [Theory]
  489. [InlineData(2147483647, -1, 2147483648)]
  490. [InlineData(-2147483647, 2, -2147483649)]
  491. public void IntegerSubtractionShouldNotOverflow(int lhs, int rhs, long result)
  492. {
  493. RunTest($"assert({lhs} - {rhs} == {result})");
  494. }
  495. [Fact]
  496. public void ToNumberHandlesStringObject()
  497. {
  498. RunTest(@"
  499. x = new String('1');
  500. x *= undefined;
  501. assert(isNaN(x));
  502. ");
  503. }
  504. [Fact]
  505. public void FunctionScopesAreChained()
  506. {
  507. RunTest(@"
  508. var x = 0;
  509. function f1(){
  510. function f2(){
  511. return x;
  512. };
  513. return f2();
  514. var x = 1;
  515. }
  516. assert(f1() === undefined);
  517. ");
  518. }
  519. [Fact]
  520. public void EvalFunctionParseAndExecuteCode()
  521. {
  522. RunTest(@"
  523. var x = 0;
  524. eval('assert(x == 0)');
  525. ");
  526. }
  527. [Fact]
  528. public void ForInStatement()
  529. {
  530. var engine = new Engine();
  531. var result = engine.Evaluate("""
  532. var x, y, str = '';
  533. for(var z in this) {
  534. str += z;
  535. }
  536. return str;
  537. """);
  538. Assert.Equal("xystrz", result);
  539. }
  540. [Fact]
  541. public void ForInStatementEnumeratesKeys()
  542. {
  543. RunTest(@"
  544. for(var i in 'abc');
  545. log(i);
  546. assert(i === '2');
  547. ");
  548. }
  549. [Fact]
  550. public void WithStatement()
  551. {
  552. RunTest(@"
  553. with (Math) {
  554. assert(cos(0) == 1);
  555. }
  556. ");
  557. }
  558. [Fact]
  559. public void ObjectExpression()
  560. {
  561. RunTest(@"
  562. var o = { x: 1 };
  563. assert(o.x == 1);
  564. ");
  565. }
  566. [Fact]
  567. public void StringFunctionCreatesString()
  568. {
  569. RunTest(@"
  570. assert(String(NaN) === 'NaN');
  571. ");
  572. }
  573. [Fact]
  574. public void ScopeChainInWithStatement()
  575. {
  576. RunTest(@"
  577. var x = 0;
  578. var myObj = {x : 'obj'};
  579. function f1(){
  580. var x = 1;
  581. function f2(){
  582. with(myObj){
  583. return x;
  584. }
  585. };
  586. return f2();
  587. }
  588. assert(f1() === 'obj');
  589. ");
  590. }
  591. [Fact]
  592. public void TryCatchBlockStatement()
  593. {
  594. RunTest(@"
  595. var x, y, z;
  596. try {
  597. x = 1;
  598. throw new TypeError();
  599. x = 2;
  600. }
  601. catch(e) {
  602. assert(x == 1);
  603. assert(e instanceof TypeError);
  604. y = 1;
  605. }
  606. finally {
  607. assert(x == 1);
  608. z = 1;
  609. }
  610. assert(x == 1);
  611. assert(y == 1);
  612. assert(z == 1);
  613. ");
  614. }
  615. [Fact]
  616. public void FunctionsCanBeAssigned()
  617. {
  618. RunTest(@"
  619. var sin = Math.sin;
  620. assert(sin(0) == 0);
  621. ");
  622. }
  623. [Fact]
  624. public void FunctionArgumentsIsDefined()
  625. {
  626. RunTest(@"
  627. function f() {
  628. assert(arguments.length > 0);
  629. }
  630. f(42);
  631. ");
  632. }
  633. [Fact]
  634. public void PrimitiveValueFunctions()
  635. {
  636. RunTest(@"
  637. var s = (1).toString();
  638. assert(s == '1');
  639. ");
  640. }
  641. [Theory]
  642. [InlineData(true, "'ab' == 'a' + 'b'")]
  643. public void OperatorsPrecedence(object expected, string source)
  644. {
  645. var engine = new Engine();
  646. var result = engine.Evaluate(source).ToObject();
  647. Assert.Equal(expected, result);
  648. }
  649. [Fact]
  650. public void FunctionPrototypeShouldHaveApplyMethod()
  651. {
  652. RunTest(@"
  653. var numbers = [5, 6, 2, 3, 7];
  654. var max = Math.max.apply(null, numbers);
  655. assert(max == 7);
  656. ");
  657. }
  658. [Theory]
  659. [InlineData(double.NaN, "parseInt(NaN)")]
  660. [InlineData(double.NaN, "parseInt(null)")]
  661. [InlineData(double.NaN, "parseInt(undefined)")]
  662. [InlineData(double.NaN, "parseInt(new Boolean(true))")]
  663. [InlineData(double.NaN, "parseInt(Infinity)")]
  664. [InlineData(-1d, "parseInt(-1)")]
  665. [InlineData(-1d, "parseInt('-1')")]
  666. [InlineData(double.NaN, "parseInt(new Array(100000).join('Z'))")]
  667. public void ShouldEvaluateParseInt(object expected, string source)
  668. {
  669. var engine = new Engine();
  670. var result = engine.Evaluate(source).ToObject();
  671. Assert.Equal(expected, result);
  672. }
  673. [Fact]
  674. public void ShouldNotExecuteDebuggerStatement()
  675. {
  676. new Engine().Evaluate("debugger");
  677. }
  678. [Fact]
  679. public void ShouldConvertDoubleToStringWithoutLosingPrecision()
  680. {
  681. RunTest(@"
  682. assert(String(14.915832707045631) === '14.915832707045631');
  683. assert(String(-14.915832707045631) === '-14.915832707045631');
  684. assert(String(0.5) === '0.5');
  685. assert(String(0.00000001) === '1e-8');
  686. assert(String(0.000001) === '0.000001');
  687. assert(String(-1.0) === '-1');
  688. assert(String(30.0) === '30');
  689. assert(String(0.2388906159889881) === '0.2388906159889881');
  690. ");
  691. }
  692. [Fact]
  693. public void ShouldWriteNumbersUsingBases()
  694. {
  695. RunTest(@"
  696. assert(15.0.toString() === '15');
  697. assert(15.0.toString(2) === '1111');
  698. assert(15.0.toString(8) === '17');
  699. assert(15.0.toString(16) === 'f');
  700. assert(15.0.toString(17) === 'f');
  701. assert(15.0.toString(36) === 'f');
  702. assert(15.1.toString(36) === 'f.3llllllllkau6snqkpygsc3di');
  703. ");
  704. }
  705. [Fact]
  706. public void ShouldNotAlterSlashesInRegex()
  707. {
  708. RunTest(@"
  709. equal('/\\//', new RegExp('/').toString());
  710. ");
  711. }
  712. [Fact]
  713. public void ShouldHandleEscapedSlashesInRegex()
  714. {
  715. RunTest(@"
  716. var regex = /[a-z]\/[a-z]/;
  717. assert(regex.test('a/b') === true);
  718. assert(regex.test('a\\/b') === false);
  719. ");
  720. }
  721. [Fact]
  722. public void ShouldComputeFractionInBase()
  723. {
  724. Assert.Equal("011", NumberPrototype.ToFractionBase(0.375, 2));
  725. Assert.Equal("14141414141414141414141414141414141414141414141414", NumberPrototype.ToFractionBase(0.375, 5));
  726. }
  727. [Fact]
  728. public void ShouldInvokeAFunctionValue()
  729. {
  730. RunTest(@"
  731. function add(x, y) { return x + y; }
  732. ");
  733. var add = _engine.GetValue("add");
  734. Assert.Equal(3, _engine.Invoke(add, 1, 2));
  735. }
  736. [Fact]
  737. public void ShouldAllowInvokeAFunctionValueWithNullValueAsArgument()
  738. {
  739. RunTest(@"
  740. function get(x) { return x; }
  741. ");
  742. var add = _engine.GetValue("get");
  743. string str = null;
  744. Assert.Equal(Native.JsValue.Null, _engine.Invoke(add, str));
  745. }
  746. [Fact]
  747. public void ShouldNotInvokeNonFunctionValue()
  748. {
  749. RunTest(@"
  750. var x= 10;
  751. ");
  752. var x = _engine.GetValue("x");
  753. var exception = Assert.Throws<JavaScriptException>(() => _engine.Invoke(x, 1, 2));
  754. Assert.Equal("Can only invoke functions", exception.Message);
  755. }
  756. [Fact]
  757. public void ShouldInvokeAFunctionValueThatBelongsToAnObject()
  758. {
  759. RunTest(@"
  760. var obj = { foo: 5, getFoo: function (bar) { return 'foo is ' + this.foo + ', bar is ' + bar; } };
  761. ");
  762. var obj = _engine.GetValue("obj").AsObject();
  763. var getFoo = obj.Get("getFoo");
  764. Assert.Equal("foo is 5, bar is 7", _engine.Invoke(getFoo, obj, new object[] { 7 }).AsString());
  765. }
  766. [Fact]
  767. public void ShouldNotInvokeNonFunctionValueThatBelongsToAnObject()
  768. {
  769. RunTest(@"
  770. var obj = { foo: 2 };
  771. ");
  772. var obj = _engine.GetValue("obj").AsObject();
  773. var foo = obj.Get("foo");
  774. Assert.Throws<JavaScriptException>(() => _engine.Invoke(foo, obj, new object[] { }));
  775. }
  776. [Fact]
  777. public void ShouldNotAllowModifyingSharedUndefinedDescriptor()
  778. {
  779. var e = new Engine();
  780. e.Evaluate("var x = { literal: true };");
  781. var pd = e.GetValue("x").AsObject().GetProperty("doesNotExist");
  782. Assert.Throws<InvalidOperationException>(() => pd.Value = "oh no, assigning this breaks things");
  783. }
  784. [Theory]
  785. [InlineData("0", 0, 16)]
  786. [InlineData("1", 1, 16)]
  787. [InlineData("100", 100, 10)]
  788. [InlineData("1100100", 100, 2)]
  789. [InlineData("2s", 100, 36)]
  790. [InlineData("2qgpckvng1s", 10000000000000000L, 36)]
  791. public void ShouldConvertNumbersToDifferentBase(string expected, long number, int radix)
  792. {
  793. var result = NumberPrototype.ToBase(number, radix);
  794. Assert.Equal(expected, result);
  795. }
  796. [Fact]
  797. public void JsonParserShouldParseNegativeNumber()
  798. {
  799. RunTest(@"
  800. var a = JSON.parse('{ ""x"":-1 }');
  801. assert(a.x === -1);
  802. var b = JSON.parse('{ ""x"": -1 }');
  803. assert(b.x === -1);
  804. ");
  805. }
  806. [Fact]
  807. public void JsonParserShouldUseToString()
  808. {
  809. RunTest(@"
  810. var a = JSON.parse(null); // Equivalent to JSON.parse('null')
  811. assert(a === null);
  812. ");
  813. RunTest(@"
  814. var a = JSON.parse(true); // Equivalent to JSON.parse('true')
  815. assert(a === true);
  816. ");
  817. RunTest(@"
  818. var a = JSON.parse(false); // Equivalent to JSON.parse('false')
  819. assert(a === false);
  820. ");
  821. RunTest(@"
  822. try {
  823. JSON.parse(undefined); // Equivalent to JSON.parse('undefined')
  824. assert(false);
  825. }
  826. catch(ex) {
  827. assert(ex instanceof SyntaxError);
  828. }
  829. ");
  830. RunTest(@"
  831. try {
  832. JSON.parse({}); // Equivalent to JSON.parse('[object Object]')
  833. assert(false);
  834. }
  835. catch(ex) {
  836. assert(ex instanceof SyntaxError);
  837. }
  838. ");
  839. RunTest(@"
  840. try {
  841. JSON.parse(function() { }); // Equivalent to JSON.parse('function () {}')
  842. assert(false);
  843. }
  844. catch(ex) {
  845. assert(ex instanceof SyntaxError);
  846. }
  847. ");
  848. }
  849. [Fact]
  850. public void JsonParserShouldDetectInvalidNegativeNumberSyntax()
  851. {
  852. RunTest(@"
  853. try {
  854. JSON.parse('{ ""x"": -.1 }'); // Not allowed
  855. assert(false);
  856. }
  857. catch(ex) {
  858. assert(ex instanceof SyntaxError);
  859. }
  860. ");
  861. RunTest(@"
  862. try {
  863. JSON.parse('{ ""x"": - 1 }'); // Not allowed
  864. assert(false);
  865. }
  866. catch(ex) {
  867. assert(ex instanceof SyntaxError);
  868. }
  869. ");
  870. }
  871. [Fact]
  872. public void JsonParserShouldUseReviverFunction()
  873. {
  874. RunTest(@"
  875. var jsonObj = JSON.parse('{""p"": 5}', function (key, value){
  876. return typeof value === 'number' ? value * 2 : value;
  877. });
  878. assert(jsonObj.p === 10);
  879. ");
  880. RunTest(@"
  881. var expectedKeys = [""1"", ""2"", ""4"", ""6"", ""5"", ""3"", """"];
  882. var actualKeys = [];
  883. JSON.parse('{""1"": 1, ""2"": 2, ""3"": {""4"": 4, ""5"": {""6"": 6}}}', function (key, value){
  884. actualKeys.push(key);
  885. return value;// return the unchanged property value.
  886. });
  887. expectedKeys.forEach(function (val, i){
  888. assert(actualKeys[i] === val);
  889. });
  890. ");
  891. }
  892. [Fact]
  893. public void JsonParserShouldHandleEmptyString()
  894. {
  895. var ex = Assert.Throws<JavaScriptException>(() => _engine.Evaluate("JSON.parse('');"));
  896. Assert.Equal("Unexpected end of JSON input at position 0", ex.Message);
  897. }
  898. [Fact]
  899. [ReplaceCulture("fr-FR")]
  900. public void ShouldBeCultureInvariant()
  901. {
  902. // decimals in french are separated by commas
  903. var engine = new Engine();
  904. var result = engine.Evaluate("1.2 + 2.1").AsNumber();
  905. Assert.Equal(3.3d, result);
  906. result = engine.Evaluate("JSON.parse('{\"x\" : 3.3}').x").AsNumber();
  907. Assert.Equal(3.3d, result);
  908. }
  909. [Fact]
  910. public void ShouldGetParseErrorLocation()
  911. {
  912. var engine = new Engine();
  913. try
  914. {
  915. engine.Evaluate("1.2+ new", "jQuery.js");
  916. }
  917. catch (ParserException e)
  918. {
  919. Assert.Equal(1, e.LineNumber);
  920. Assert.Equal(9, e.Column);
  921. Assert.Equal("jQuery.js", e.SourceLocation);
  922. }
  923. }
  924. #region DateParsingAndStrings
  925. [Fact]
  926. public void ParseShouldReturnNumber()
  927. {
  928. var engine = new Engine();
  929. var result = engine.Evaluate("Date.parse('1970-01-01');").AsNumber();
  930. Assert.Equal(0, result);
  931. }
  932. [Fact]
  933. public void TimeWithinDayShouldHandleNegativeValues()
  934. {
  935. RunTest(@"
  936. // using a date < 1970 so that the primitive value is negative
  937. var d = new Date(1958, 0, 1);
  938. d.setMonth(-1);
  939. assert(d.getDate() == 1);
  940. ");
  941. }
  942. [Fact]
  943. public void LocalDateTimeShouldNotLoseTimezone()
  944. {
  945. var date = new DateTime(2016, 1, 1, 13, 0, 0, DateTimeKind.Local);
  946. var engine = new Engine().SetValue("localDate", date);
  947. var actual = engine.Evaluate(@"localDate").AsDate().ToDateTime();
  948. Assert.Equal(date.ToUniversalTime(), actual.ToUniversalTime());
  949. Assert.Equal(date.ToLocalTime(), actual.ToLocalTime());
  950. }
  951. [Fact]
  952. public void UtcShouldUseUtc()
  953. {
  954. var customTimeZone = _tongaTimeZone;
  955. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
  956. var result = engine.Evaluate("Date.UTC(1970,0,1)").AsNumber();
  957. Assert.Equal(0, result);
  958. }
  959. [Fact]
  960. public void ShouldUseLocalTimeZoneOverride()
  961. {
  962. const string customName = "Custom Time";
  963. var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, 11, 0), customName, customName, customName, null, false);
  964. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
  965. var epochGetLocalMinutes = engine.Evaluate("var d = new Date(0); d.getMinutes();").AsNumber();
  966. Assert.Equal(11, epochGetLocalMinutes);
  967. var localEpochGetUtcMinutes = engine.Evaluate("var d = new Date(1970,0,1); d.getUTCMinutes();").AsNumber();
  968. Assert.Equal(49, localEpochGetUtcMinutes);
  969. var parseLocalEpoch = engine.Evaluate("Date.parse('January 1, 1970');").AsNumber();
  970. Assert.Equal(-11 * 60 * 1000, parseLocalEpoch);
  971. var epochToLocalString = engine.Evaluate("var d = new Date(0); d.toString();").AsString();
  972. Assert.Equal("Thu Jan 01 1970 00:11:00 GMT+0011 (Custom Time)", epochToLocalString);
  973. var epochToUTCString = engine.Evaluate("var d = new Date(0); d.toUTCString();").AsString();
  974. Assert.Equal("Thu, 01 Jan 1970 00:00:00 GMT", epochToUTCString);
  975. }
  976. [Theory]
  977. [InlineData("1970")]
  978. [InlineData("1970-01")]
  979. [InlineData("1970-01-01")]
  980. [InlineData("1970-01-01T00:00Z")]
  981. [InlineData("1970-01-01T00:00:00Z")]
  982. [InlineData("1970-01-01T00:00:00.000Z")]
  983. [InlineData("1970Z")]
  984. [InlineData("1970-1Z")]
  985. [InlineData("1970-1-1Z")]
  986. [InlineData("1970-1-1T0:0Z")]
  987. [InlineData("1970-1-1T0:0:0Z")]
  988. [InlineData("1970-1-1T0:0:0.0Z")]
  989. [InlineData("1970/1Z")]
  990. [InlineData("1970/1/1Z")]
  991. [InlineData("1970/1/1 0:0Z")]
  992. [InlineData("1970/1/1 0:0:0Z")]
  993. [InlineData("1970/1/1 0:0:0.0Z")]
  994. [InlineData("January 1, 1970 GMT")]
  995. [InlineData("1970-01-01T00:00:00.000-00:00")]
  996. public void ShouldParseAsUtc(string date)
  997. {
  998. var customTimeZone = _tongaTimeZone;
  999. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone));
  1000. engine.SetValue("d", date);
  1001. var result = engine.Evaluate("Date.parse(d);").AsNumber();
  1002. Assert.Equal(0, result);
  1003. }
  1004. [Theory]
  1005. [InlineData("1970-01-01T00:00")]
  1006. [InlineData("1970-01-01T00:00:00")]
  1007. [InlineData("1970-01-01T00:00:00.000")]
  1008. [InlineData("1970/01")]
  1009. [InlineData("1970/01/01")]
  1010. [InlineData("1970/01/01T00:00")]
  1011. [InlineData("1970/01/01 00:00")]
  1012. [InlineData("1970-1")]
  1013. [InlineData("1970-1-1")]
  1014. [InlineData("1970-1-1T0:0")]
  1015. [InlineData("1970-1-1 0:0")]
  1016. [InlineData("1970/1")]
  1017. [InlineData("1970/1/1")]
  1018. [InlineData("1970/1/1T0:0")]
  1019. [InlineData("1970/1/1 0:0")]
  1020. [InlineData("01-1970")]
  1021. [InlineData("01-01-1970")]
  1022. [InlineData("January 1, 1970")]
  1023. [InlineData("1970-01-01T00:00:00.000+00:11")]
  1024. public void ShouldParseAsLocalTime(string date)
  1025. {
  1026. const int timespanMinutes = 11;
  1027. const int msPriorMidnight = -timespanMinutes * 60 * 1000;
  1028. const string customName = "Custom Time";
  1029. var customTimeZone = TimeZoneInfo.CreateCustomTimeZone(customName, new TimeSpan(0, timespanMinutes, 0), customName, customName, customName, null, false);
  1030. var engine = new Engine(cfg => cfg.LocalTimeZone(customTimeZone)).SetValue("d", date);
  1031. var result = engine.Evaluate("Date.parse(d);").AsNumber();
  1032. Assert.Equal(msPriorMidnight, result);
  1033. }
  1034. public static System.Collections.Generic.IEnumerable<object[]> TestDates
  1035. {
  1036. get
  1037. {
  1038. yield return new object[] { new DateTime(2000, 1, 1) };
  1039. yield return new object[] { new DateTime(2000, 1, 1, 0, 15, 15, 15) };
  1040. yield return new object[] { new DateTime(2000, 6, 1, 0, 15, 15, 15) };
  1041. yield return new object[] { new DateTime(1900, 1, 1) };
  1042. yield return new object[] { new DateTime(1900, 1, 1, 0, 15, 15, 15) };
  1043. yield return new object[] { new DateTime(1900, 6, 1, 0, 15, 15, 15) };
  1044. }
  1045. }
  1046. [Theory, MemberData("TestDates")]
  1047. public void TestDateToISOStringFormat(DateTime testDate)
  1048. {
  1049. var customTimeZone = _pacificTimeZone;
  1050. var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone));
  1051. var testDateTimeOffset = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate));
  1052. engine.Execute(
  1053. string.Format("var d = new Date({0},{1},{2},{3},{4},{5},{6});", testDateTimeOffset.Year, testDateTimeOffset.Month - 1, testDateTimeOffset.Day, testDateTimeOffset.Hour, testDateTimeOffset.Minute, testDateTimeOffset.Second, testDateTimeOffset.Millisecond));
  1054. Assert.Equal(testDateTimeOffset.UtcDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture), engine.Evaluate("d.toISOString();").ToString());
  1055. }
  1056. [Theory, MemberData(nameof(TestDates))]
  1057. public void TestDateToStringFormat(DateTime testDate)
  1058. {
  1059. var customTimeZone = _pacificTimeZone;
  1060. var engine = new Engine(ctx => ctx.LocalTimeZone(customTimeZone));
  1061. var dt = new DateTimeOffset(testDate, customTimeZone.GetUtcOffset(testDate));
  1062. var dateScript = $"var d = new Date({dt.Year}, {dt.Month - 1}, {dt.Day}, {dt.Hour}, {dt.Minute}, {dt.Second}, {dt.Millisecond});";
  1063. engine.Execute(dateScript);
  1064. var expected = dt.ToString("ddd MMM dd yyyy HH:mm:ss", CultureInfo.InvariantCulture);
  1065. expected += dt.ToString(" 'GMT'zzz", CultureInfo.InvariantCulture).Replace(":", "");
  1066. expected += " (Pacific Standard Time)";
  1067. var actual = engine.Evaluate("d.toString();").ToString();
  1068. Assert.Equal(expected, actual);
  1069. }
  1070. #endregion
  1071. //DateParsingAndStrings
  1072. [Fact]
  1073. public void EmptyStringShouldMatchRegex()
  1074. {
  1075. RunTest(@"
  1076. var regex = /^(?:$)/g;
  1077. assert(''.match(regex) instanceof Array);
  1078. ");
  1079. }
  1080. [Fact]
  1081. public void ShouldExecuteHandlebars()
  1082. {
  1083. var content = GetEmbeddedFile("handlebars.js");
  1084. RunTest(content);
  1085. RunTest(@"
  1086. var source = 'Hello {{name}}';
  1087. var template = Handlebars.compile(source);
  1088. var context = {name: 'Paul'};
  1089. var html = template(context);
  1090. assert('Hello Paul' == html);
  1091. ");
  1092. }
  1093. [Fact]
  1094. public void ShouldExecutePrism()
  1095. {
  1096. var content = GetEmbeddedFile("prism.js");
  1097. RunTest(content);
  1098. RunTest(@"
  1099. var input = 'using System; public class Person { public int Name { get; set; } }';
  1100. var lang = 'csharp';
  1101. var highlighted = Prism.highlight(input, Prism.languages.csharp, lang);
  1102. assert(highlighted.includes('System'));
  1103. assert(highlighted.includes('Person'));
  1104. assert(highlighted.includes('Name'));
  1105. log(highlighted);
  1106. ");
  1107. _engine.SetValue("input", File.ReadAllText("../../../../Jint/Engine.cs"));
  1108. RunTest("Prism.highlight(input, Prism.languages.csharp, lang);");
  1109. }
  1110. [Fact]
  1111. public void ShouldExecuteDromaeoBase64()
  1112. {
  1113. RunTest(@"
  1114. var startTest = function () { };
  1115. var test = function (name, fn) { fn(); };
  1116. var endTest = function () { };
  1117. var prep = function (fn) { fn(); };
  1118. ");
  1119. var content = GetEmbeddedFile("dromaeo-string-base64.js");
  1120. RunTest(content);
  1121. }
  1122. [Fact]
  1123. public void ShouldExecuteKnockoutWithoutErrorWhetherTolerantOrIntolerant()
  1124. {
  1125. var content = GetEmbeddedFile("knockout-3.4.0.js");
  1126. _engine.Execute(content, new ParserOptions { Tolerant = true });
  1127. _engine.Execute(content, new ParserOptions { Tolerant = false });
  1128. }
  1129. [Fact]
  1130. public void ShouldAllowProtoProperty()
  1131. {
  1132. var code = "if({ __proto__: [] } instanceof Array) {}";
  1133. _engine.Execute(code);
  1134. _engine.Execute($"eval('{code}')");
  1135. _engine.Execute($"new Function('{code}')");
  1136. }
  1137. [Fact]
  1138. public void ShouldNotAllowDuplicateProtoProperty()
  1139. {
  1140. var code = "if({ __proto__: [], __proto__:[] } instanceof Array) {}";
  1141. Exception ex = Assert.Throws<ParserException>(() => _engine.Execute(code, new ParserOptions { Tolerant = false }));
  1142. Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message);
  1143. ex = Assert.Throws<JavaScriptException>(() => _engine.Execute($"eval('{code}')"));
  1144. Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message);
  1145. Assert.Throws<JavaScriptException>(() => _engine.Execute($"new Function('{code}')"));
  1146. Assert.Contains("Duplicate __proto__ fields are not allowed in object literals", ex.Message);
  1147. }
  1148. [Fact]
  1149. public void ShouldExecuteLodash()
  1150. {
  1151. var content = GetEmbeddedFile("lodash.min.js");
  1152. RunTest(content);
  1153. }
  1154. [Fact]
  1155. public void DateParseReturnsNaN()
  1156. {
  1157. RunTest(@"
  1158. var d = Date.parse('not a date');
  1159. assert(isNaN(d));
  1160. ");
  1161. }
  1162. [Fact]
  1163. public void ShouldIgnoreHtmlComments()
  1164. {
  1165. RunTest(@"
  1166. var d = Date.parse('not a date'); <!-- a comment -->
  1167. assert(isNaN(d));
  1168. ");
  1169. }
  1170. [Fact]
  1171. public void DateShouldAllowEntireDotNetDateRange()
  1172. {
  1173. var engine = new Engine();
  1174. var minValue = engine.Evaluate("new Date('0001-01-01T00:00:00.000Z')").ToObject();
  1175. Assert.Equal(new DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc), minValue);
  1176. var maxValue = engine.Evaluate("new Date('9999-12-31T23:59:59.999Z')").ToObject();
  1177. #if NETCOREAPP
  1178. Assert.Equal(new DateTime(9999, 12, 31, 23, 59, 59, 998, DateTimeKind.Utc), maxValue);
  1179. #else
  1180. Assert.Equal(new DateTime(9999, 12, 31, 23, 59, 59, 999, DateTimeKind.Utc), maxValue);
  1181. #endif
  1182. }
  1183. [Fact]
  1184. public void ShouldConstructNewArrayWithInteger()
  1185. {
  1186. RunTest(@"
  1187. var a = new Array(3);
  1188. assert(a.length === 3);
  1189. assert(a[0] == undefined);
  1190. assert(a[1] == undefined);
  1191. assert(a[2] == undefined);
  1192. ");
  1193. }
  1194. [Fact]
  1195. public void ShouldConstructNewArrayWithString()
  1196. {
  1197. RunTest(@"
  1198. var a = new Array('foo');
  1199. assert(a.length === 1);
  1200. assert(a[0] === 'foo');
  1201. ");
  1202. }
  1203. [Fact]
  1204. public void ShouldThrowRangeExceptionWhenConstructedWithNonInteger()
  1205. {
  1206. RunTest(@"
  1207. var result = false;
  1208. try {
  1209. var a = new Array(3.4);
  1210. }
  1211. catch(e) {
  1212. result = e instanceof RangeError;
  1213. }
  1214. assert(result);
  1215. ");
  1216. }
  1217. [Fact]
  1218. public void ShouldInitializeArrayWithSingleIngegerValue()
  1219. {
  1220. RunTest(@"
  1221. var a = [3];
  1222. assert(a.length === 1);
  1223. assert(a[0] === 3);
  1224. ");
  1225. }
  1226. [Fact]
  1227. public void ShouldInitializeJsonObjectArrayWithSingleIntegerValue()
  1228. {
  1229. RunTest(@"
  1230. var x = JSON.parse('{ ""a"": [3] }');
  1231. assert(x.a.length === 1);
  1232. assert(x.a[0] === 3);
  1233. ");
  1234. }
  1235. [Fact]
  1236. public void ShouldInitializeJsonArrayWithSingleIntegerValue()
  1237. {
  1238. RunTest(@"
  1239. var a = JSON.parse('[3]');
  1240. assert(a.length === 1);
  1241. assert(a[0] === 3);
  1242. ");
  1243. }
  1244. [Fact]
  1245. public void ShouldReturnTrueForEmptyIsNaNStatement()
  1246. {
  1247. RunTest(@"
  1248. assert(true === isNaN());
  1249. ");
  1250. }
  1251. [Theory]
  1252. [InlineData(4d, 0, "4")]
  1253. [InlineData(4d, 1, "4.0")]
  1254. [InlineData(4d, 2, "4.00")]
  1255. [InlineData(28.995, 2, "29.00")]
  1256. [InlineData(-28.995, 2, "-29.00")]
  1257. [InlineData(-28.495, 2, "-28.50")]
  1258. [InlineData(-28.445, 2, "-28.45")]
  1259. [InlineData(28.445, 2, "28.45")]
  1260. [InlineData(10.995, 0, "11")]
  1261. public void ShouldRoundToFixedDecimal(double number, int fractionDigits, string result)
  1262. {
  1263. var engine = new Engine();
  1264. var value = engine.Evaluate(
  1265. String.Format("new Number({0}).toFixed({1})",
  1266. number.ToString(CultureInfo.InvariantCulture),
  1267. fractionDigits.ToString(CultureInfo.InvariantCulture)))
  1268. .ToObject();
  1269. Assert.Equal(value, result);
  1270. }
  1271. [Fact]
  1272. public void ShouldSortArrayWhenCompareFunctionReturnsFloatingPointNumber()
  1273. {
  1274. RunTest(@"
  1275. var nums = [1, 1.1, 1.2, 2, 2, 2.1, 2.2];
  1276. nums.sort(function(a,b){return b-a;});
  1277. assert(nums[0] === 2.2);
  1278. assert(nums[1] === 2.1);
  1279. assert(nums[2] === 2);
  1280. assert(nums[3] === 2);
  1281. assert(nums[4] === 1.2);
  1282. assert(nums[5] === 1.1);
  1283. assert(nums[6] === 1);
  1284. ");
  1285. }
  1286. [Fact]
  1287. public void ShouldBreakWhenBreakpointIsReached()
  1288. {
  1289. countBreak = 0;
  1290. stepMode = StepMode.None;
  1291. var engine = new Engine(options => options.DebugMode());
  1292. engine.Debugger.Break += EngineStep;
  1293. engine.Debugger.BreakPoints.Set(new BreakPoint(1, 0));
  1294. engine.Evaluate(@"var local = true;
  1295. if (local === true)
  1296. {}");
  1297. engine.Debugger.Break -= EngineStep;
  1298. Assert.Equal(1, countBreak);
  1299. }
  1300. [Fact]
  1301. public void ShouldExecuteStepByStep()
  1302. {
  1303. countBreak = 0;
  1304. stepMode = StepMode.Into;
  1305. var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode));
  1306. engine.Debugger.Step += EngineStep;
  1307. engine.Evaluate(@"var local = true;
  1308. var creatingSomeOtherLine = 0;
  1309. var lastOneIPromise = true");
  1310. engine.Debugger.Step -= EngineStep;
  1311. Assert.Equal(3, countBreak);
  1312. }
  1313. [Fact]
  1314. public void ShouldNotBreakTwiceIfSteppingOverBreakpoint()
  1315. {
  1316. countBreak = 0;
  1317. stepMode = StepMode.Into;
  1318. var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode));
  1319. engine.Debugger.BreakPoints.Set(new BreakPoint(1, 1));
  1320. engine.Debugger.Step += EngineStep;
  1321. engine.Debugger.Break += EngineStep;
  1322. engine.Evaluate(@"var local = true;");
  1323. engine.Debugger.Step -= EngineStep;
  1324. engine.Debugger.Break -= EngineStep;
  1325. Assert.Equal(1, countBreak);
  1326. }
  1327. private StepMode EngineStep(object sender, DebugInformation debugInfo)
  1328. {
  1329. Assert.NotNull(sender);
  1330. Assert.IsType(typeof(Engine), sender);
  1331. Assert.NotNull(debugInfo);
  1332. countBreak++;
  1333. return stepMode;
  1334. }
  1335. [Fact]
  1336. public void ShouldShowProperDebugInformation()
  1337. {
  1338. countBreak = 0;
  1339. stepMode = StepMode.None;
  1340. var engine = new Engine(options => options.DebugMode());
  1341. engine.Debugger.BreakPoints.Set(new BreakPoint(5, 0));
  1342. engine.Debugger.Break += EngineStepVerifyDebugInfo;
  1343. engine.Evaluate(@"var global = true;
  1344. function func1()
  1345. {
  1346. var local = false;
  1347. ;
  1348. }
  1349. func1();");
  1350. engine.Debugger.Break -= EngineStepVerifyDebugInfo;
  1351. Assert.Equal(1, countBreak);
  1352. }
  1353. private StepMode EngineStepVerifyDebugInfo(object sender, DebugInformation debugInfo)
  1354. {
  1355. Assert.NotNull(sender);
  1356. Assert.IsType(typeof(Engine), sender);
  1357. Assert.NotNull(debugInfo);
  1358. Assert.NotNull(debugInfo.CallStack);
  1359. Assert.NotNull(debugInfo.CurrentNode);
  1360. Assert.NotNull(debugInfo.CurrentScopeChain);
  1361. Assert.Equal(2, debugInfo.CallStack.Count);
  1362. Assert.Equal("func1", debugInfo.CurrentCallFrame.FunctionName);
  1363. var globalScope = debugInfo.CurrentScopeChain.Single(s => s.ScopeType == DebugScopeType.Global);
  1364. var localScope = debugInfo.CurrentScopeChain.Single(s => s.ScopeType == DebugScopeType.Local);
  1365. Assert.Contains("global", globalScope.BindingNames);
  1366. Assert.Equal(true, globalScope.GetBindingValue("global").AsBoolean());
  1367. Assert.Contains("local", localScope.BindingNames);
  1368. Assert.Equal(false, localScope.GetBindingValue("local").AsBoolean());
  1369. Assert.DoesNotContain("global", localScope.BindingNames);
  1370. countBreak++;
  1371. return stepMode;
  1372. }
  1373. [Fact]
  1374. public void ShouldBreakWhenConditionIsMatched()
  1375. {
  1376. countBreak = 0;
  1377. stepMode = StepMode.None;
  1378. var engine = new Engine(options => options.DebugMode());
  1379. engine.Debugger.Break += EngineStep;
  1380. engine.Debugger.BreakPoints.Set(new BreakPoint(5, 16, "condition === true"));
  1381. engine.Debugger.BreakPoints.Set(new BreakPoint(6, 16, "condition === false"));
  1382. engine.Evaluate(@"var local = true;
  1383. var condition = true;
  1384. if (local === true)
  1385. {
  1386. ;
  1387. ;
  1388. }");
  1389. engine.Debugger.Break -= EngineStep;
  1390. Assert.Equal(1, countBreak);
  1391. }
  1392. [Fact]
  1393. public void ShouldNotStepInSameLevelStatementsWhenStepOut()
  1394. {
  1395. countBreak = 0;
  1396. stepMode = StepMode.Out;
  1397. var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into));
  1398. engine.Debugger.Step += EngineStep;
  1399. engine.Evaluate(@"function func() // first step - then stepping out
  1400. {
  1401. ; // shall not step
  1402. ; // not even here
  1403. }
  1404. func(); // shall not step
  1405. ; // shall not step ");
  1406. engine.Debugger.Step -= EngineStep;
  1407. Assert.Equal(1, countBreak);
  1408. }
  1409. [Fact]
  1410. public void ShouldNotStepInIfRequiredToStepOut()
  1411. {
  1412. countBreak = 0;
  1413. var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into));
  1414. engine.Debugger.Step += EngineStepOutWhenInsideFunction;
  1415. engine.Evaluate(@"function func() // first step
  1416. {
  1417. ; // third step - now stepping out
  1418. ; // it should not step here
  1419. }
  1420. func(); // second step
  1421. ; // fourth step ");
  1422. engine.Debugger.Step -= EngineStepOutWhenInsideFunction;
  1423. Assert.Equal(4, countBreak);
  1424. }
  1425. private StepMode EngineStepOutWhenInsideFunction(object sender, DebugInformation debugInfo)
  1426. {
  1427. Assert.NotNull(sender);
  1428. Assert.IsType(typeof(Engine), sender);
  1429. Assert.NotNull(debugInfo);
  1430. countBreak++;
  1431. if (debugInfo.CallStack.Count > 1) // CallStack always has at least one element
  1432. return StepMode.Out;
  1433. return StepMode.Into;
  1434. }
  1435. [Fact]
  1436. public void ShouldBreakWhenStatementIsMultiLine()
  1437. {
  1438. countBreak = 0;
  1439. stepMode = StepMode.None;
  1440. var engine = new Engine(options => options.DebugMode());
  1441. engine.Debugger.BreakPoints.Set(new BreakPoint(4, 32));
  1442. engine.Debugger.Break += EngineStep;
  1443. engine.Evaluate(@"var global = true;
  1444. function func1()
  1445. {
  1446. var local =
  1447. false;
  1448. }
  1449. func1();");
  1450. engine.Debugger.Break -= EngineStep;
  1451. Assert.Equal(1, countBreak);
  1452. }
  1453. [Fact]
  1454. public void ShouldNotStepInsideIfRequiredToStepOver()
  1455. {
  1456. countBreak = 0;
  1457. stepMode = StepMode.Over;
  1458. var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode));
  1459. engine.Debugger.Step += EngineStep;
  1460. engine.Evaluate(@"function func() // first step
  1461. {
  1462. ; // third step - it shall not step here
  1463. ; // it shall not step here
  1464. }
  1465. func(); // second step
  1466. ; // third step ");
  1467. engine.Debugger.Step -= EngineStep;
  1468. Assert.Equal(3, countBreak);
  1469. }
  1470. [Fact]
  1471. public void ShouldStepAllStatementsWithoutInvocationsIfStepOver()
  1472. {
  1473. countBreak = 0;
  1474. stepMode = StepMode.Over;
  1475. var engine = new Engine(options => options.DebugMode().InitialStepMode(stepMode));
  1476. engine.Debugger.Step += EngineStep;
  1477. engine.Evaluate(@"var step1 = 1; // first step
  1478. var step2 = 2; // second step
  1479. if (step1 !== step2) // third step
  1480. {
  1481. ; // fourth step
  1482. }");
  1483. engine.Debugger.Step -= EngineStep;
  1484. Assert.Equal(4, countBreak);
  1485. }
  1486. [Fact]
  1487. public void ShouldEvaluateVariableAssignmentFromLeftToRight()
  1488. {
  1489. RunTest(@"
  1490. var keys = ['a']
  1491. , source = { a: 3}
  1492. , target = {}
  1493. , key
  1494. , i = 0;
  1495. target[key = keys[i++]] = source[key];
  1496. equal(1, i);
  1497. equal('a', key);
  1498. equal(3, target[key]);
  1499. ");
  1500. }
  1501. [Fact]
  1502. public void ObjectShouldBeExtensible()
  1503. {
  1504. RunTest(@"
  1505. try {
  1506. Object.defineProperty(Object.defineProperty, 'foo', { value: 1 });
  1507. }
  1508. catch(e) {
  1509. assert(false);
  1510. }
  1511. ");
  1512. }
  1513. [Fact]
  1514. public void ArrayIndexShouldBeConvertedToUint32()
  1515. {
  1516. // This is missing from ECMA tests suite
  1517. // http://www.ecma-international.org/ecma-262/5.1/#sec-15.4
  1518. RunTest(@"
  1519. var a = [ 'foo' ];
  1520. assert(a[0] === 'foo');
  1521. assert(a['0'] === 'foo');
  1522. assert(a['00'] === undefined);
  1523. ");
  1524. }
  1525. [Fact]
  1526. public void HexZeroAsArrayIndexShouldWork()
  1527. {
  1528. var engine = new Engine();
  1529. engine.Evaluate("var t = '1234'; var value = null;");
  1530. Assert.Equal("1", engine.Execute("value = t[0x0];").GetValue("value").AsString());
  1531. Assert.Equal("1", engine.Execute("value = t[0];").GetValue("value").AsString());
  1532. Assert.Equal("1", engine.Execute("value = t['0'];").GetValue("value").AsString());
  1533. }
  1534. [Fact]
  1535. public void DatePrototypeFunctionWorkOnDateOnly()
  1536. {
  1537. RunTest(@"
  1538. try {
  1539. var myObj = Object.create(Date.prototype);
  1540. myObj.toDateString();
  1541. } catch (e) {
  1542. assert(e instanceof TypeError);
  1543. }
  1544. ");
  1545. }
  1546. [Fact]
  1547. public void DateToStringMethodsShouldUseCurrentTimeZoneAndCulture()
  1548. {
  1549. // Forcing to PDT and FR for tests
  1550. // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time");
  1551. var PDT = _pacificTimeZone;
  1552. var FR = new CultureInfo("fr-FR");
  1553. var engine = new Engine(options => options.LocalTimeZone(PDT).Culture(FR))
  1554. .SetValue("log", new Action<object>(Console.WriteLine))
  1555. .SetValue("assert", new Action<bool>(Assert.True))
  1556. .SetValue("equal", new Action<object, object>(Assert.Equal))
  1557. ;
  1558. engine.Evaluate(@"
  1559. var d = new Date(1433160000000);
  1560. equal('Mon Jun 01 2015 05:00:00 GMT-0700 (Pacific Standard Time)', d.toString());
  1561. equal('Mon Jun 01 2015', d.toDateString());
  1562. equal('05:00:00 GMT-0700 (Pacific Standard Time)', d.toTimeString());
  1563. equal('lundi 1 juin 2015 05:00:00', d.toLocaleString());
  1564. equal('lundi 1 juin 2015', d.toLocaleDateString());
  1565. equal('05:00:00', d.toLocaleTimeString());
  1566. ");
  1567. }
  1568. [Fact]
  1569. public void DateShouldHonorTimezoneDaylightSavingRules()
  1570. {
  1571. var EST = _easternTimeZone;
  1572. var engine = new Engine(options => options.LocalTimeZone(EST))
  1573. .SetValue("log", new Action<object>(Console.WriteLine))
  1574. .SetValue("assert", new Action<bool>(Assert.True))
  1575. .SetValue("equal", new Action<object, object>(Assert.Equal));
  1576. engine.Evaluate(@"
  1577. var d = new Date(2016, 8, 1);
  1578. // there's a Linux difference, so do a replace
  1579. equal('Thu Sep 01 2016 00:00:00 GMT-0400 (US Eastern Standard Time)', d.toString().replace('(Eastern Standard Time)', '(US Eastern Standard Time)'));
  1580. equal('Thu Sep 01 2016', d.toDateString());
  1581. ");
  1582. }
  1583. [Fact]
  1584. public void DateShouldParseToString()
  1585. {
  1586. // Forcing to PDT and FR for tests
  1587. // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time");
  1588. var PDT = _pacificTimeZone;
  1589. var FR = new CultureInfo("fr-FR");
  1590. new Engine(options => options.LocalTimeZone(PDT).Culture(FR))
  1591. .SetValue("log", new Action<object>(Console.WriteLine))
  1592. .SetValue("assert", new Action<bool>(Assert.True))
  1593. .SetValue("equal", new Action<object, object>(Assert.Equal))
  1594. .Evaluate(@"
  1595. var d = new Date(1433160000000);
  1596. equal(Date.parse(d.toString()), d.valueOf());
  1597. equal(Date.parse(d.toLocaleString()), d.valueOf());
  1598. ");
  1599. }
  1600. [Fact]
  1601. public void ShouldThrowErrorWhenMaxExecutionStackCountLimitExceeded()
  1602. {
  1603. new Engine(options => options.Constraints.MaxExecutionStackCount = 1000)
  1604. .SetValue("assert", new Action<bool>(Assert.True))
  1605. .Evaluate(@"
  1606. var count = 0;
  1607. function recurse() {
  1608. count++;
  1609. recurse();
  1610. return null; // ensure no tail recursion
  1611. }
  1612. try {
  1613. count = 0;
  1614. recurse();
  1615. assert(false);
  1616. } catch(err) {
  1617. assert(count >= 1000);
  1618. }
  1619. ");
  1620. }
  1621. [Fact]
  1622. public void LocaleNumberShouldUseLocalCulture()
  1623. {
  1624. // Forcing to PDT and FR for tests
  1625. // var PDT = TimeZoneInfo.CreateCustomTimeZone("Pacific Daylight Time", new TimeSpan(-7, 0, 0), "Pacific Daylight Time", "Pacific Daylight Time");
  1626. var PDT = _pacificTimeZone;
  1627. var FR = new CultureInfo("fr-FR");
  1628. var engine = new Engine(options => options.LocalTimeZone(PDT).Culture(FR))
  1629. .SetValue("log", new Action<object>(Console.WriteLine))
  1630. .SetValue("assert", new Action<bool>(Assert.True))
  1631. .SetValue("equal", new Action<object, object>(Assert.Equal));
  1632. engine.Evaluate("var d = new Number(-1.23);");
  1633. engine.Evaluate("equal('-1.23', d.toString());");
  1634. // NET 5 globalization APIs use ICU libraries on newer Windows 10 giving different result
  1635. // build server is older Windows...
  1636. engine.Evaluate("assert('-1,230' === d.toLocaleString() || '-1,23' === d.toLocaleString());");
  1637. }
  1638. [Fact]
  1639. public void DateCtorShouldAcceptDate()
  1640. {
  1641. RunTest(@"
  1642. var a = new Date();
  1643. var b = new Date(a);
  1644. assert(String(a) === String(b));
  1645. ");
  1646. }
  1647. [Fact]
  1648. public void RegExpResultIsMutable()
  1649. {
  1650. RunTest(@"
  1651. var match = /quick\s(brown).+?(jumps)/ig.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
  1652. var result = match.shift();
  1653. assert(result === 'Quick Brown Fox Jumps');
  1654. ");
  1655. }
  1656. [Fact]
  1657. public void RegExpSupportsMultiline()
  1658. {
  1659. RunTest(@"
  1660. var rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg;
  1661. var headersString = 'X-AspNetMvc-Version: 4.0\r\nX-Powered-By: ASP.NET\r\n\r\n';
  1662. match = rheaders.exec(headersString);
  1663. assert('X-AspNetMvc-Version' === match[1]);
  1664. assert('4.0' === match[2]);
  1665. ");
  1666. RunTest(@"
  1667. var rheaders = /^(.*?):[ \t]*(.*?)$/mg;
  1668. var headersString = 'X-AspNetMvc-Version: 4.0\r\nX-Powered-By: ASP.NET\r\n\r\n';
  1669. match = rheaders.exec(headersString);
  1670. assert('X-AspNetMvc-Version' === match[1]);
  1671. assert('4.0' === match[2]);
  1672. ");
  1673. RunTest(@"
  1674. var rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg;
  1675. var headersString = 'X-AspNetMvc-Version: 4.0\nX-Powered-By: ASP.NET\n\n';
  1676. match = rheaders.exec(headersString);
  1677. assert('X-AspNetMvc-Version' === match[1]);
  1678. assert('4.0' === match[2]);
  1679. ");
  1680. }
  1681. [Fact]
  1682. public void RegExpPrototypeToString()
  1683. {
  1684. RunTest("assert(RegExp.prototype.toString() === '/(?:)/');");
  1685. }
  1686. [Fact]
  1687. public void ShouldSetYearBefore1970()
  1688. {
  1689. RunTest(@"
  1690. var d = new Date('1969-01-01T08:17:00Z');
  1691. d.setYear(2015);
  1692. equal('2015-01-01T08:17:00.000Z', d.toISOString());
  1693. ");
  1694. }
  1695. [Fact]
  1696. public void ShouldUseReplaceMarkers()
  1697. {
  1698. RunTest(@"
  1699. var re = /a/g;
  1700. var str = 'abab';
  1701. var newstr = str.replace(re, '$\'x');
  1702. equal('babxbbxb', newstr);
  1703. ");
  1704. }
  1705. [Fact]
  1706. public void ExceptionShouldHaveLocationOfInnerFunction()
  1707. {
  1708. var engine = new Engine();
  1709. const string source = @"
  1710. function test(s) {
  1711. o.boom();
  1712. }
  1713. test('arg');
  1714. ";
  1715. var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(source));
  1716. Assert.Equal(3, ex.Location.Start.Line);
  1717. }
  1718. [Fact]
  1719. public void GlobalRegexLiteralShouldNotKeepState()
  1720. {
  1721. RunTest(@"
  1722. var url = 'https://www.example.com';
  1723. assert(isAbsolutePath(url));
  1724. assert(isAbsolutePath(url));
  1725. assert(isAbsolutePath(url));
  1726. function isAbsolutePath(path) {
  1727. return /\.+/g.test(path);
  1728. }
  1729. ");
  1730. }
  1731. [Fact]
  1732. public void ShouldCompareInnerValueOfClrInstances()
  1733. {
  1734. var engine = new Engine();
  1735. // Create two separate Guid with identical inner values.
  1736. var guid1 = Guid.NewGuid();
  1737. var guid2 = new Guid(guid1.ToString());
  1738. engine.SetValue("guid1", guid1);
  1739. engine.SetValue("guid2", guid2);
  1740. var result = engine.Evaluate("guid1 == guid2").AsBoolean();
  1741. Assert.True(result);
  1742. }
  1743. [Fact]
  1744. public void CanStringifyToConsole()
  1745. {
  1746. var engine = new Engine(options => options.AllowClr(typeof(Console).Assembly));
  1747. engine.Evaluate("System.Console.WriteLine(JSON.stringify({x:12, y:14}));");
  1748. }
  1749. [Fact]
  1750. public void ShouldNotCompareClrInstancesWithObjects()
  1751. {
  1752. var engine = new Engine();
  1753. var guid1 = Guid.NewGuid();
  1754. engine.SetValue("guid1", guid1);
  1755. var result = engine.Evaluate("guid1 == {}").AsBoolean();
  1756. Assert.False(result);
  1757. }
  1758. [Fact]
  1759. public void ShouldStringifyNumWithoutV8DToA()
  1760. {
  1761. // 53.6841659 cannot be converted by V8's DToA => "old" DToA code will be used.
  1762. var engine = new Engine();
  1763. var val = engine.Evaluate("JSON.stringify(53.6841659)");
  1764. Assert.Equal("53.6841659", val.AsString());
  1765. }
  1766. [Fact]
  1767. public void ShouldStringifyObjectWithPropertiesToSameRef()
  1768. {
  1769. var engine = new Engine();
  1770. var res = engine.Evaluate(@"
  1771. var obj = {
  1772. a : [],
  1773. a1 : ['str'],
  1774. a2 : {},
  1775. a3 : { 'prop' : 'val' }
  1776. };
  1777. obj.b = obj.a;
  1778. obj.b1 = obj.a1;
  1779. JSON.stringify(obj);
  1780. ");
  1781. Assert.True(res == "{\"a\":[],\"a1\":[\"str\"],\"a2\":{},\"a3\":{\"prop\":\"val\"},\"b\":[],\"b1\":[\"str\"]}");
  1782. }
  1783. [Fact]
  1784. public void ShouldThrowOnSerializingCyclicRefObject()
  1785. {
  1786. var engine = new Engine();
  1787. var res = engine.Evaluate(@"
  1788. (function(){
  1789. try{
  1790. a = [];
  1791. a[0] = a;
  1792. my_text = JSON.stringify(a);
  1793. }
  1794. catch(ex){
  1795. return ex.message;
  1796. }
  1797. })();
  1798. ");
  1799. Assert.True(res == "Cyclic reference detected.");
  1800. }
  1801. [Fact]
  1802. public void ShouldNotStringifyFunctionValuedProperties()
  1803. {
  1804. var engine = new Engine();
  1805. var res = engine.Evaluate(@"
  1806. var obj = {
  1807. f: function() { }
  1808. };
  1809. return JSON.stringify(obj);
  1810. ");
  1811. Assert.Equal("{}", res.AsString());
  1812. }
  1813. [Theory]
  1814. [InlineData("", "escape('')")]
  1815. [InlineData("%u0100%u0101%u0102", "escape('\u0100\u0101\u0102')")]
  1816. [InlineData("%uFFFD%uFFFE%uFFFF", "escape('\ufffd\ufffe\uffff')")]
  1817. [InlineData("%uD834%uDF06", "escape('\ud834\udf06')")]
  1818. [InlineData("%00%01%02%03", "escape('\x00\x01\x02\x03')")]
  1819. [InlineData("%2C", "escape(',')")]
  1820. [InlineData("%3A%3B%3C%3D%3E%3F", "escape(':;<=>?')")]
  1821. [InlineData("%60", "escape('`')")]
  1822. [InlineData("%7B%7C%7D%7E%7F%80", "escape('{|}~\x7f\x80')")]
  1823. [InlineData("%FD%FE%FF", "escape('\xfd\xfe\xff')")]
  1824. [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./", "escape('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./')")]
  1825. public void ShouldEvaluateEscape(object expected, string source)
  1826. {
  1827. var engine = new Engine();
  1828. var result = engine.Evaluate(source).ToObject();
  1829. Assert.Equal(expected, result);
  1830. }
  1831. [Theory]
  1832. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/empty-string.js
  1833. [InlineData("", "unescape('')")]
  1834. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-bad-u.js
  1835. [InlineData("%U0000", "unescape('%U0000')")]
  1836. [InlineData("%t0000", "unescape('%t0000')")]
  1837. [InlineData("%v0000", "unescape('%v0000')")]
  1838. [InlineData("%" + "\x00" + "00", "unescape('%%0000')")]
  1839. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-end-str.js
  1840. [InlineData("%u", "unescape('%u')")]
  1841. [InlineData("%u0", "unescape('%u0')")]
  1842. [InlineData("%u1", "unescape('%u1')")]
  1843. [InlineData("%u2", "unescape('%u2')")]
  1844. [InlineData("%u3", "unescape('%u3')")]
  1845. [InlineData("%u4", "unescape('%u4')")]
  1846. [InlineData("%u5", "unescape('%u5')")]
  1847. [InlineData("%u6", "unescape('%u6')")]
  1848. [InlineData("%u7", "unescape('%u7')")]
  1849. [InlineData("%u8", "unescape('%u8')")]
  1850. [InlineData("%u9", "unescape('%u9')")]
  1851. [InlineData("%ua", "unescape('%ua')")]
  1852. [InlineData("%uA", "unescape('%uA')")]
  1853. [InlineData("%ub", "unescape('%ub')")]
  1854. [InlineData("%uB", "unescape('%uB')")]
  1855. [InlineData("%uc", "unescape('%uc')")]
  1856. [InlineData("%uC", "unescape('%uC')")]
  1857. [InlineData("%ud", "unescape('%ud')")]
  1858. [InlineData("%uD", "unescape('%uD')")]
  1859. [InlineData("%ue", "unescape('%ue')")]
  1860. [InlineData("%uE", "unescape('%uE')")]
  1861. [InlineData("%uf", "unescape('%uf')")]
  1862. [InlineData("%uF", "unescape('%uF')")]
  1863. [InlineData("%u01", "unescape('%u01')")]
  1864. [InlineData("%u02", "unescape('%u02')")]
  1865. [InlineData("%u03", "unescape('%u03')")]
  1866. [InlineData("%u04", "unescape('%u04')")]
  1867. [InlineData("%u05", "unescape('%u05')")]
  1868. [InlineData("%u06", "unescape('%u06')")]
  1869. [InlineData("%u07", "unescape('%u07')")]
  1870. [InlineData("%u08", "unescape('%u08')")]
  1871. [InlineData("%u09", "unescape('%u09')")]
  1872. [InlineData("%u0a", "unescape('%u0a')")]
  1873. [InlineData("%u0A", "unescape('%u0A')")]
  1874. [InlineData("%u0b", "unescape('%u0b')")]
  1875. [InlineData("%u0B", "unescape('%u0B')")]
  1876. [InlineData("%u0c", "unescape('%u0c')")]
  1877. [InlineData("%u0C", "unescape('%u0C')")]
  1878. [InlineData("%u0d", "unescape('%u0d')")]
  1879. [InlineData("%u0D", "unescape('%u0D')")]
  1880. [InlineData("%u0e", "unescape('%u0e')")]
  1881. [InlineData("%u0E", "unescape('%u0E')")]
  1882. [InlineData("%u0f", "unescape('%u0f')")]
  1883. [InlineData("%u0F", "unescape('%u0F')")]
  1884. [InlineData("%u000", "unescape('%u000')")]
  1885. [InlineData("%u001", "unescape('%u001')")]
  1886. [InlineData("%u002", "unescape('%u002')")]
  1887. [InlineData("%u003", "unescape('%u003')")]
  1888. [InlineData("%u004", "unescape('%u004')")]
  1889. [InlineData("%u005", "unescape('%u005')")]
  1890. [InlineData("%u006", "unescape('%u006')")]
  1891. [InlineData("%u007", "unescape('%u007')")]
  1892. [InlineData("%u008", "unescape('%u008')")]
  1893. [InlineData("%u009", "unescape('%u009')")]
  1894. [InlineData("%u00a", "unescape('%u00a')")]
  1895. [InlineData("%u00A", "unescape('%u00A')")]
  1896. [InlineData("%u00b", "unescape('%u00b')")]
  1897. [InlineData("%u00B", "unescape('%u00B')")]
  1898. [InlineData("%u00c", "unescape('%u00c')")]
  1899. [InlineData("%u00C", "unescape('%u00C')")]
  1900. [InlineData("%u00d", "unescape('%u00d')")]
  1901. [InlineData("%u00D", "unescape('%u00D')")]
  1902. [InlineData("%u00e", "unescape('%u00e')")]
  1903. [InlineData("%u00E", "unescape('%u00E')")]
  1904. [InlineData("%u00f", "unescape('%u00f')")]
  1905. [InlineData("%u00F", "unescape('%u00F')")]
  1906. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four-ignore-non-hex.js
  1907. [InlineData("%u000%0", "unescape('%u000%0')")]
  1908. [InlineData("%u000g0", "unescape('%u000g0')")]
  1909. [InlineData("%u000G0", "unescape('%u000G0')")]
  1910. [InlineData("%u00g00", "unescape('%u00g00')")]
  1911. [InlineData("%u00G00", "unescape('%u00G00')")]
  1912. [InlineData("%u0g000", "unescape('%u0g000')")]
  1913. [InlineData("%u0G000", "unescape('%u0G000')")]
  1914. [InlineData("%ug0000", "unescape('%ug0000')")]
  1915. [InlineData("%uG0000", "unescape('%uG0000')")]
  1916. [InlineData("%u000u0", "unescape('%u000u0')")]
  1917. [InlineData("%u000U0", "unescape('%u000U0')")]
  1918. [InlineData("%u00u00", "unescape('%u00u00')")]
  1919. [InlineData("%u00U00", "unescape('%u00U00')")]
  1920. [InlineData("%u0u000", "unescape('%u0u000')")]
  1921. [InlineData("%u0U000", "unescape('%u0U000')")]
  1922. [InlineData("%uu0000", "unescape('%uu0000')")]
  1923. [InlineData("%uU0000", "unescape('%uU0000')")]
  1924. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/four.js
  1925. [InlineData("%0" + "\x00" + "0", "unescape('%0%u00000')")]
  1926. [InlineData("%0" + "\x01" + "0", "unescape('%0%u00010')")]
  1927. [InlineData("%0)0", "unescape('%0%u00290')")]
  1928. [InlineData("%0*0", "unescape('%0%u002a0')")]
  1929. [InlineData("%0*0", "unescape('%0%u002A0')")]
  1930. [InlineData("%0+0", "unescape('%0%u002b0')")]
  1931. [InlineData("%0+0", "unescape('%0%u002B0')")]
  1932. [InlineData("%0,0", "unescape('%0%u002c0')")]
  1933. [InlineData("%0,0", "unescape('%0%u002C0')")]
  1934. [InlineData("%0-0", "unescape('%0%u002d0')")]
  1935. [InlineData("%0-0", "unescape('%0%u002D0')")]
  1936. [InlineData("%090", "unescape('%0%u00390')")]
  1937. [InlineData("%0:0", "unescape('%0%u003a0')")]
  1938. [InlineData("%0:0", "unescape('%0%u003A0')")]
  1939. [InlineData("%0?0", "unescape('%0%u003f0')")]
  1940. [InlineData("%0?0", "unescape('%0%u003F0')")]
  1941. [InlineData("%0@0", "unescape('%0%u00400')")]
  1942. [InlineData("%0Z0", "unescape('%0%u005a0')")]
  1943. [InlineData("%0Z0", "unescape('%0%u005A0')")]
  1944. [InlineData("%0[0", "unescape('%0%u005b0')")]
  1945. [InlineData("%0[0", "unescape('%0%u005B0')")]
  1946. [InlineData("%0^0", "unescape('%0%u005e0')")]
  1947. [InlineData("%0^0", "unescape('%0%u005E0')")]
  1948. [InlineData("%0_0", "unescape('%0%u005f0')")]
  1949. [InlineData("%0_0", "unescape('%0%u005F0')")]
  1950. [InlineData("%0`0", "unescape('%0%u00600')")]
  1951. [InlineData("%0a0", "unescape('%0%u00610')")]
  1952. [InlineData("%0z0", "unescape('%0%u007a0')")]
  1953. [InlineData("%0z0", "unescape('%0%u007A0')")]
  1954. [InlineData("%0{0", "unescape('%0%u007b0')")]
  1955. [InlineData("%0{0", "unescape('%0%u007B0')")]
  1956. [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffe0')")]
  1957. [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFffe0')")]
  1958. [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufFfe0')")]
  1959. [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uffFe0')")]
  1960. [InlineData("%0" + "\ufffe" + "0", "unescape('%0%ufffE0')")]
  1961. [InlineData("%0" + "\ufffe" + "0", "unescape('%0%uFFFE0')")]
  1962. [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffff0')")]
  1963. [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFfff0')")]
  1964. [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufFff0')")]
  1965. [InlineData("%0" + "\uffff" + "0", "unescape('%0%uffFf0')")]
  1966. [InlineData("%0" + "\uffff" + "0", "unescape('%0%ufffF0')")]
  1967. [InlineData("%0" + "\uffff" + "0", "unescape('%0%uFFFF0')")]
  1968. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-end-str.js
  1969. [InlineData("%", "unescape('%')")]
  1970. [InlineData("%0", "unescape('%0')")]
  1971. [InlineData("%1", "unescape('%1')")]
  1972. [InlineData("%2", "unescape('%2')")]
  1973. [InlineData("%3", "unescape('%3')")]
  1974. [InlineData("%4", "unescape('%4')")]
  1975. [InlineData("%5", "unescape('%5')")]
  1976. [InlineData("%6", "unescape('%6')")]
  1977. [InlineData("%7", "unescape('%7')")]
  1978. [InlineData("%8", "unescape('%8')")]
  1979. [InlineData("%9", "unescape('%9')")]
  1980. [InlineData("%a", "unescape('%a')")]
  1981. [InlineData("%A", "unescape('%A')")]
  1982. [InlineData("%b", "unescape('%b')")]
  1983. [InlineData("%B", "unescape('%B')")]
  1984. [InlineData("%c", "unescape('%c')")]
  1985. [InlineData("%C", "unescape('%C')")]
  1986. [InlineData("%d", "unescape('%d')")]
  1987. [InlineData("%D", "unescape('%D')")]
  1988. [InlineData("%e", "unescape('%e')")]
  1989. [InlineData("%E", "unescape('%E')")]
  1990. [InlineData("%f", "unescape('%f')")]
  1991. [InlineData("%F", "unescape('%F')")]
  1992. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two-ignore-non-hex.js
  1993. [InlineData("%0%0", "unescape('%0%0')")]
  1994. [InlineData("%0g0", "unescape('%0g0')")]
  1995. [InlineData("%0G0", "unescape('%0G0')")]
  1996. [InlineData("%g00", "unescape('%g00')")]
  1997. [InlineData("%G00", "unescape('%G00')")]
  1998. [InlineData("%0u0", "unescape('%0u0')")]
  1999. [InlineData("%0U0", "unescape('%0U0')")]
  2000. [InlineData("%u00", "unescape('%u00')")]
  2001. [InlineData("%U00", "unescape('%U00')")]
  2002. //https://github.com/tc39/test262/blob/master/test/annexB/built-ins/unescape/two.js
  2003. [InlineData("%0" + "\x00" + "00", "unescape('%0%0000')")]
  2004. [InlineData("%0" + "\x01" + "00", "unescape('%0%0100')")]
  2005. [InlineData("%0)00", "unescape('%0%2900')")]
  2006. [InlineData("%0*00", "unescape('%0%2a00')")]
  2007. [InlineData("%0*00", "unescape('%0%2A00')")]
  2008. [InlineData("%0+00", "unescape('%0%2b00')")]
  2009. [InlineData("%0+00", "unescape('%0%2B00')")]
  2010. [InlineData("%0,00", "unescape('%0%2c00')")]
  2011. [InlineData("%0,00", "unescape('%0%2C00')")]
  2012. [InlineData("%0-00", "unescape('%0%2d00')")]
  2013. [InlineData("%0-00", "unescape('%0%2D00')")]
  2014. [InlineData("%0900", "unescape('%0%3900')")]
  2015. [InlineData("%0:00", "unescape('%0%3a00')")]
  2016. [InlineData("%0:00", "unescape('%0%3A00')")]
  2017. [InlineData("%0?00", "unescape('%0%3f00')")]
  2018. [InlineData("%0?00", "unescape('%0%3F00')")]
  2019. [InlineData("%0@00", "unescape('%0%4000')")]
  2020. [InlineData("%0Z00", "unescape('%0%5a00')")]
  2021. [InlineData("%0Z00", "unescape('%0%5A00')")]
  2022. [InlineData("%0[00", "unescape('%0%5b00')")]
  2023. [InlineData("%0[00", "unescape('%0%5B00')")]
  2024. [InlineData("%0^00", "unescape('%0%5e00')")]
  2025. [InlineData("%0^00", "unescape('%0%5E00')")]
  2026. [InlineData("%0_00", "unescape('%0%5f00')")]
  2027. [InlineData("%0_00", "unescape('%0%5F00')")]
  2028. [InlineData("%0`00", "unescape('%0%6000')")]
  2029. [InlineData("%0a00", "unescape('%0%6100')")]
  2030. [InlineData("%0z00", "unescape('%0%7a00')")]
  2031. [InlineData("%0z00", "unescape('%0%7A00')")]
  2032. [InlineData("%0{00", "unescape('%0%7b00')")]
  2033. [InlineData("%0{00", "unescape('%0%7B00')")]
  2034. public void ShouldEvaluateUnescape(object expected, string source)
  2035. {
  2036. var engine = new Engine();
  2037. var result = engine.Evaluate(source).ToObject();
  2038. Assert.Equal(expected, result);
  2039. }
  2040. [Theory]
  2041. [InlineData("new Date(1969,0,1,19,45,30,500).getHours()", 19)]
  2042. [InlineData("new Date(1970,0,1,19,45,30,500).getHours()", 19)]
  2043. [InlineData("new Date(1971,0,1,19,45,30,500).getHours()", 19)]
  2044. [InlineData("new Date(1969,0,1,19,45,30,500).getMinutes()", 45)]
  2045. [InlineData("new Date(1970,0,1,19,45,30,500).getMinutes()", 45)]
  2046. [InlineData("new Date(1971,0,1,19,45,30,500).getMinutes()", 45)]
  2047. [InlineData("new Date(1969,0,1,19,45,30,500).getSeconds()", 30)]
  2048. [InlineData("new Date(1970,0,1,19,45,30,500).getSeconds()", 30)]
  2049. [InlineData("new Date(1971,0,1,19,45,30,500).getSeconds()", 30)]
  2050. //[InlineData("new Date(1969,0,1,19,45,30,500).getMilliseconds()", 500)]
  2051. //[InlineData("new Date(1970,0,1,19,45,30,500).getMilliseconds()", 500)]
  2052. //[InlineData("new Date(1971,0,1,19,45,30,500).getMilliseconds()", 500)]
  2053. public void ShouldExtractDateParts(string source, double expected)
  2054. {
  2055. var engine = new Engine();
  2056. var result = engine.Evaluate(source).ToObject();
  2057. Assert.Equal(expected, result);
  2058. }
  2059. [Theory]
  2060. [InlineData("'abc'.padStart(10)", " abc")]
  2061. [InlineData("'abc'.padStart(10, \"foo\")", "foofoofabc")]
  2062. [InlineData("'abc'.padStart(6, \"123456\")", "123abc")]
  2063. [InlineData("'abc'.padStart(8, \"0\")", "00000abc")]
  2064. [InlineData("'abc'.padStart(1)", "abc")]
  2065. public void ShouldPadStart(string source, object expected)
  2066. {
  2067. var engine = new Engine();
  2068. var result = engine.Evaluate(source).ToObject();
  2069. Assert.Equal(expected, result);
  2070. }
  2071. [Theory]
  2072. [InlineData("'abc'.padEnd(10)", "abc ")]
  2073. [InlineData("'abc'.padEnd(10, \"foo\")", "abcfoofoof")]
  2074. [InlineData("'abc'.padEnd(6, \"123456\")", "abc123")]
  2075. [InlineData("'abc'.padEnd(1)", "abc")]
  2076. public void ShouldPadEnd(string source, object expected)
  2077. {
  2078. var engine = new Engine();
  2079. var result = engine.Evaluate(source).ToObject();
  2080. Assert.Equal(expected, result);
  2081. }
  2082. /// <summary>
  2083. /// Tests for startsWith - tests created from MDN and https://github.com/mathiasbynens/String.prototype.startsWith/blob/master/tests/tests.js
  2084. /// </summary>
  2085. [Theory]
  2086. [InlineData("'To be, or not to be, that is the question.'.startsWith('To be')", true)]
  2087. [InlineData("'To be, or not to be, that is the question.'.startsWith('not to be')", false)]
  2088. [InlineData("'To be, or not to be, that is the question.'.startsWith()", false)]
  2089. [InlineData("'To be, or not to be, that is the question.'.startsWith('not to be', 10)", true)]
  2090. [InlineData("'undefined'.startsWith()", true)]
  2091. [InlineData("'undefined'.startsWith(undefined)", true)]
  2092. [InlineData("'undefined'.startsWith(null)", false)]
  2093. [InlineData("'null'.startsWith()", false)]
  2094. [InlineData("'null'.startsWith(undefined)", false)]
  2095. [InlineData("'null'.startsWith(null)", true)]
  2096. [InlineData("'abc'.startsWith()", false)]
  2097. [InlineData("'abc'.startsWith('')", true)]
  2098. [InlineData("'abc'.startsWith('\0')", false)]
  2099. [InlineData("'abc'.startsWith('a')", true)]
  2100. [InlineData("'abc'.startsWith('b')", false)]
  2101. [InlineData("'abc'.startsWith('ab')", true)]
  2102. [InlineData("'abc'.startsWith('bc')", false)]
  2103. [InlineData("'abc'.startsWith('abc')", true)]
  2104. [InlineData("'abc'.startsWith('bcd')", false)]
  2105. [InlineData("'abc'.startsWith('abcd')", false)]
  2106. [InlineData("'abc'.startsWith('bcde')", false)]
  2107. [InlineData("'abc'.startsWith('', 1)", true)]
  2108. [InlineData("'abc'.startsWith('\0', 1)", false)]
  2109. [InlineData("'abc'.startsWith('a', 1)", false)]
  2110. [InlineData("'abc'.startsWith('b', 1)", true)]
  2111. [InlineData("'abc'.startsWith('ab', 1)", false)]
  2112. [InlineData("'abc'.startsWith('bc', 1)", true)]
  2113. [InlineData("'abc'.startsWith('abc', 1)", false)]
  2114. [InlineData("'abc'.startsWith('bcd', 1)", false)]
  2115. [InlineData("'abc'.startsWith('abcd', 1)", false)]
  2116. [InlineData("'abc'.startsWith('bcde', 1)", false)]
  2117. public void ShouldStartWith(string source, object expected)
  2118. {
  2119. var engine = new Engine();
  2120. var result = engine.Evaluate(source).ToObject();
  2121. Assert.Equal(expected, result);
  2122. }
  2123. [Theory]
  2124. [InlineData("throw {}", "undefined")]
  2125. [InlineData("throw {message:null}", "null")]
  2126. [InlineData("throw {message:''}", "")]
  2127. [InlineData("throw {message:2}", "2")]
  2128. public void ShouldAllowNonStringMessage(string source, string expected)
  2129. {
  2130. var engine = new Engine();
  2131. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(source));
  2132. Assert.Equal(expected, ex.Message);
  2133. }
  2134. [Theory]
  2135. //Months
  2136. [InlineData("new Date(2017, 0, 1, 0, 0, 0)", "new Date(2016, 12, 1, 0, 0, 0)")]
  2137. [InlineData("new Date(2016, 0, 1, 23, 59, 59)", "new Date(2015, 12, 1, 23, 59, 59)")]
  2138. [InlineData("new Date(2013, 0, 1, 0, 0, 0)", "new Date(2012, 12, 1, 0, 0, 0)")]
  2139. [InlineData("new Date(2013, 0, 29, 23, 59, 59)", "new Date(2012, 12, 29, 23, 59, 59)")]
  2140. [InlineData("new Date(2015, 11, 1, 0, 0, 0)", "new Date(2016, -1, 1, 0, 0, 0)")]
  2141. [InlineData("new Date(2014, 11, 1, 23, 59, 59)", "new Date(2015, -1, 1, 23, 59, 59)")]
  2142. [InlineData("new Date(2011, 11, 1, 0, 0, 0)", "new Date(2012, -1, 1, 0, 0, 0)")]
  2143. [InlineData("new Date(2011, 11, 29, 23, 59, 59)", "new Date(2012, -1, 29, 23, 59, 59)")]
  2144. [InlineData("new Date(2015, 1, 1, 0, 0, 0)", "new Date(2016, -11, 1, 0, 0, 0)")]
  2145. [InlineData("new Date(2014, 1, 1, 23, 59, 59)", "new Date(2015, -11, 1, 23, 59, 59)")]
  2146. [InlineData("new Date(2011, 1, 1, 0, 0, 0)", "new Date(2012, -11, 1, 0, 0, 0)")]
  2147. [InlineData("new Date(2011, 2, 1, 23, 59, 59)", "new Date(2012, -11, 29, 23, 59, 59)")]
  2148. [InlineData("new Date(2015, 0, 1, 0, 0, 0)", "new Date(2016, -12, 1, 0, 0, 0)")]
  2149. [InlineData("new Date(2014, 0, 1, 23, 59, 59)", "new Date(2015, -12, 1, 23, 59, 59)")]
  2150. [InlineData("new Date(2011, 0, 1, 0, 0, 0)", "new Date(2012, -12, 1, 0, 0, 0)")]
  2151. [InlineData("new Date(2011, 0, 29, 23, 59, 59)", "new Date(2012, -12, 29, 23, 59, 59)")]
  2152. [InlineData("new Date(2014, 11, 1, 0, 0, 0)", "new Date(2016, -13, 1, 0, 0, 0)")]
  2153. [InlineData("new Date(2013, 11, 1, 23, 59, 59)", "new Date(2015, -13, 1, 23, 59, 59)")]
  2154. [InlineData("new Date(2010, 11, 1, 0, 0, 0)", "new Date(2012, -13, 1, 0, 0, 0)")]
  2155. [InlineData("new Date(2010, 11, 29, 23, 59, 59)", "new Date(2012, -13, 29, 23, 59, 59)")]
  2156. [InlineData("new Date(2013, 11, 1, 0, 0, 0)", "new Date(2016, -25, 1, 0, 0, 0)")]
  2157. [InlineData("new Date(2012, 11, 1, 23, 59, 59)", "new Date(2015, -25, 1, 23, 59, 59)")]
  2158. [InlineData("new Date(2009, 11, 1, 0, 0, 0)", "new Date(2012, -25, 1, 0, 0, 0)")]
  2159. [InlineData("new Date(2009, 11, 29, 23, 59, 59)", "new Date(2012, -25, 29, 23, 59, 59)")]
  2160. //Days
  2161. [InlineData("new Date(2016, 1, 11, 0, 0, 0)", "new Date(2016, 0, 42, 0, 0, 0)")]
  2162. [InlineData("new Date(2016, 0, 11, 23, 59, 59)", "new Date(2015, 11, 42, 23, 59, 59)")]
  2163. [InlineData("new Date(2012, 3, 11, 0, 0, 0)", "new Date(2012, 2, 42, 0, 0, 0)")]
  2164. [InlineData("new Date(2012, 2, 13, 23, 59, 59)", "new Date(2012, 1, 42, 23, 59, 59)")]
  2165. [InlineData("new Date(2015, 11, 31, 0, 0, 0)", "new Date(2016, 0, 0, 0, 0, 0)")]
  2166. [InlineData("new Date(2015, 10, 30, 23, 59, 59)", "new Date(2015, 11, 0, 23, 59, 59)")]
  2167. [InlineData("new Date(2012, 1, 29, 0, 0, 0)", "new Date(2012, 2, 0, 0, 0, 0)")]
  2168. [InlineData("new Date(2012, 0, 31, 23, 59, 59)", "new Date(2012, 1, 0, 23, 59, 59)")]
  2169. [InlineData("new Date(2015, 10, 24, 0, 0, 0)", "new Date(2016, 0, -37, 0, 0, 0)")]
  2170. [InlineData("new Date(2015, 9, 24, 23, 59, 59)", "new Date(2015, 11, -37, 23, 59, 59)")]
  2171. [InlineData("new Date(2012, 0, 23, 0, 0, 0)", "new Date(2012, 2, -37, 0, 0, 0)")]
  2172. [InlineData("new Date(2011, 11, 25, 23, 59, 59)", "new Date(2012, 1, -37, 23, 59, 59)")]
  2173. //Hours
  2174. [InlineData("new Date(2016, 0, 2, 1, 0, 0)", "new Date(2016, 0, 1, 25, 0, 0)")]
  2175. [InlineData("new Date(2015, 11, 2, 1, 59, 59)", "new Date(2015, 11, 1, 25, 59, 59)")]
  2176. [InlineData("new Date(2012, 2, 2, 1, 0, 0)", "new Date(2012, 2, 1, 25, 0, 0)")]
  2177. [InlineData("new Date(2012, 2, 1, 1, 59, 59)", "new Date(2012, 1, 29, 25, 59, 59)")]
  2178. [InlineData("new Date(2016, 0, 19, 3, 0, 0)", "new Date(2016, 0, 1, 435, 0, 0)")]
  2179. [InlineData("new Date(2015, 11, 19, 3, 59, 59)", "new Date(2015, 11, 1, 435, 59, 59)")]
  2180. [InlineData("new Date(2012, 2, 19, 3, 0, 0)", "new Date(2012, 2, 1, 435, 0, 0)")]
  2181. [InlineData("new Date(2012, 2, 18, 3, 59, 59)", "new Date(2012, 1, 29, 435, 59, 59)")]
  2182. [InlineData("new Date(2015, 11, 31, 23, 0, 0)", "new Date(2016, 0, 1, -1, 0, 0)")]
  2183. [InlineData("new Date(2015, 10, 30, 23, 59, 59)", "new Date(2015, 11, 1, -1, 59, 59)")]
  2184. [InlineData("new Date(2012, 1, 29, 23, 0, 0)", "new Date(2012, 2, 1, -1, 0, 0)")]
  2185. [InlineData("new Date(2012, 1, 28, 23, 59, 59)", "new Date(2012, 1, 29, -1, 59, 59)")]
  2186. [InlineData("new Date(2015, 11, 3, 18, 0, 0)", "new Date(2016, 0, 1, -678, 0, 0)")]
  2187. [InlineData("new Date(2015, 10, 2, 18, 59, 59)", "new Date(2015, 11, 1, -678, 59, 59)")]
  2188. [InlineData("new Date(2012, 1, 1, 18, 0, 0)", "new Date(2012, 2, 1, -678, 0, 0)")]
  2189. [InlineData("new Date(2012, 0, 31, 18, 59, 59)", "new Date(2012, 1, 29, -678, 59, 59)")]
  2190. // Minutes
  2191. [InlineData("new Date(2016, 0, 1, 1, 0, 0)", "new Date(2016, 0, 1, 0, 60, 0)")]
  2192. [InlineData("new Date(2015, 11, 2, 0, 0, 59)", "new Date(2015, 11, 1, 23, 60, 59)")]
  2193. [InlineData("new Date(2012, 2, 1, 1, 0, 0)", "new Date(2012, 2, 1, 0, 60, 0)")]
  2194. [InlineData("new Date(2012, 2, 1, 0, 0, 59)", "new Date(2012, 1, 29, 23, 60, 59)")]
  2195. [InlineData("new Date(2015, 11, 31, 23, 59, 0)", "new Date(2016, 0, 1, 0, -1, 0)")]
  2196. [InlineData("new Date(2015, 11, 1, 22, 59, 59)", "new Date(2015, 11, 1, 23, -1, 59)")]
  2197. [InlineData("new Date(2012, 1, 29, 23, 59, 0)", "new Date(2012, 2, 1, 0, -1, 0)")]
  2198. [InlineData("new Date(2012, 1, 29, 22, 59, 59)", "new Date(2012, 1, 29, 23, -1, 59)")]
  2199. [InlineData("new Date(2016, 0, 2, 15, 5, 0)", "new Date(2016, 0, 1, 0, 2345, 0)")]
  2200. [InlineData("new Date(2015, 11, 3, 14, 5, 59)", "new Date(2015, 11, 1, 23, 2345, 59)")]
  2201. [InlineData("new Date(2012, 2, 2, 15, 5, 0)", "new Date(2012, 2, 1, 0, 2345, 0)")]
  2202. [InlineData("new Date(2012, 2, 2, 14, 5, 59)", "new Date(2012, 1, 29, 23, 2345, 59)")]
  2203. [InlineData("new Date(2015, 11, 25, 18, 24, 0)", "new Date(2016, 0, 1, 0, -8976, 0)")]
  2204. [InlineData("new Date(2015, 10, 25, 17, 24, 59)", "new Date(2015, 11, 1, 23, -8976, 59)")]
  2205. [InlineData("new Date(2012, 1, 23, 18, 24, 0)", "new Date(2012, 2, 1, 0, -8976, 0)")]
  2206. [InlineData("new Date(2012, 1, 23, 17, 24, 59)", "new Date(2012, 1, 29, 23, -8976, 59)")]
  2207. // Seconds
  2208. [InlineData("new Date(2016, 0, 1, 0, 1, 0)", "new Date(2016, 0, 1, 0, 0, 60)")]
  2209. [InlineData("new Date(2015, 11, 2, 0, 0, 0)", "new Date(2015, 11, 1, 23, 59, 60)")]
  2210. [InlineData("new Date(2012, 2, 1, 0, 1, 0)", "new Date(2012, 2, 1, 0, 0, 60)")]
  2211. [InlineData("new Date(2012, 2, 1, 0, 0, 0)", "new Date(2012, 1, 29, 23, 59, 60)")]
  2212. [InlineData("new Date(2015, 11, 31, 23, 59, 59)", "new Date(2016, 0, 1, 0, 0, -1)")]
  2213. [InlineData("new Date(2015, 11, 1, 23, 58, 59)", "new Date(2015, 11, 1, 23, 59, -1)")]
  2214. [InlineData("new Date(2012, 1, 29, 23, 59, 59)", "new Date(2012, 2, 1, 0, 0, -1)")]
  2215. [InlineData("new Date(2012, 1, 29, 23, 58, 59)", "new Date(2012, 1, 29, 23, 59, -1)")]
  2216. [InlineData("new Date(2016, 0, 3, 17, 9, 58)", "new Date(2016, 0, 1, 0, 0, 234598)")]
  2217. [InlineData("new Date(2015, 11, 4, 17, 8, 58)", "new Date(2015, 11, 1, 23, 59, 234598)")]
  2218. [InlineData("new Date(2012, 2, 3, 17, 9, 58)", "new Date(2012, 2, 1, 0, 0, 234598)")]
  2219. [InlineData("new Date(2012, 2, 3, 17, 8, 58)", "new Date(2012, 1, 29, 23, 59, 234598)")]
  2220. [InlineData("new Date(2015, 11, 21, 14, 39, 15)", "new Date(2016, 0, 1, 0, 0, -897645)")]
  2221. [InlineData("new Date(2015, 10, 21, 14, 38, 15)", "new Date(2015, 11, 1, 23, 59, -897645)")]
  2222. [InlineData("new Date(2012, 1, 19, 14, 39, 15)", "new Date(2012, 2, 1, 0, 0, -897645)")]
  2223. [InlineData("new Date(2012, 1, 19, 14, 38, 15)", "new Date(2012, 1, 29, 23, 59, -897645)")]
  2224. public void ShouldSupportDateConsturctorWithArgumentOutOfRange(string expected, string actual)
  2225. {
  2226. var engine = new Engine(o => o.LocalTimeZone(TimeZoneInfo.Utc));
  2227. var expectedValue = engine.Evaluate(expected).ToObject();
  2228. var actualValue = engine.Evaluate(actual).ToObject();
  2229. Assert.Equal(expectedValue, actualValue);
  2230. }
  2231. [Fact]
  2232. public void ShouldReturnCorrectConcatenatedStrings()
  2233. {
  2234. RunTest(@"
  2235. function concat(x, a, b) {
  2236. x += a;
  2237. x += b;
  2238. return x;
  2239. }");
  2240. var concat = _engine.GetValue("concat");
  2241. var result = _engine.Invoke(concat, "concat", "well", "done").ToObject() as string;
  2242. Assert.Equal("concatwelldone", result);
  2243. }
  2244. [Fact]
  2245. public void ComplexMappingAndReducing()
  2246. {
  2247. const string program = @"
  2248. Object.map = function (o, f, ctx) {
  2249. ctx = ctx || this;
  2250. var result = [];
  2251. Object.keys(o).forEach(function(k) {
  2252. result.push(f.call(ctx, o[k], k));
  2253. });
  2254. return result;
  2255. };
  2256. var x1 = {""Value"":1.0,""Elements"":[{""Name"":""a"",""Value"":""b"",""Decimal"":3.2},{""Name"":""a"",""Value"":""b"",""Decimal"": 3.5}],""Values"":{""test"": 2,""test1"":3,""test2"": 4}}
  2257. var x2 = {""Value"":2.0,""Elements"":[{""Name"":""aa"",""Value"":""ba"",""Decimal"":3.5}],""Values"":{""test"":1,""test1"":2,""test2"":3}};
  2258. function output(x) {
  2259. var elements = x.Elements.map(function(a){return a.Decimal;});
  2260. var values = x.Values;
  2261. var generated = x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {});
  2262. return {
  2263. TestDictionary1 : values,
  2264. TestDictionary2 : x.Values,
  2265. TestDictionaryDirectAccess1 : Object.keys(x.Values).length,
  2266. TestDictionaryDirectAccess2 : Object.keys(x.Values),
  2267. TestDictionaryDirectAccess4 : Object.keys(x.Values).map(function(a){return x.Values[a];}),
  2268. TestDictionarySum1 : Object.keys(values).map(function(a){return{Key: a,Value:values[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0),
  2269. TestDictionarySum2 : Object.keys(x.Values).map(function(a){return{Key: a,Value:x.Values[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0),
  2270. TestDictionarySum3 : Object.keys(x.Values).map(function(a){return x.Values[a];}).reduce(function(a, b) { return a + b; }, 0),
  2271. TestDictionaryAverage1 : Object.keys(values).map(function(a){return{Key: a,Value:values[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0)/(Object.keys(values).length||1),
  2272. TestDictionaryAverage2 : Object.keys(x.Values).map(function(a){return{Key: a,Value:x.Values[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0)/(Object.keys(x.Values).length||1),
  2273. TestDictionaryAverage3 : Object.keys(x.Values).map(function(a){return x.Values[a];}).reduce(function(a, b) { return a + b; }, 0)/(Object.keys(x.Values).map(function(a){return x.Values[a];}).length||1),
  2274. TestDictionaryFunc1 : Object.keys(x.Values).length,
  2275. TestDictionaryFunc2 : Object.map(x.Values, function(v, k){ return v;}),
  2276. TestGeneratedDictionary1 : generated,
  2277. TestGeneratedDictionary2 : x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {}),
  2278. TestGeneratedDictionary3 : Object.keys(generated).length,
  2279. TestGeneratedDictionarySum1 : Object.keys(generated).map(function(a){return{Key: a,Value:generated[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0),
  2280. TestGeneratedDictionarySum2 : Object.keys(x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {})).map(function(a){return{Key: a,Value:x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {})[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0),
  2281. TestGeneratedDictionaryAverage1 : Object.keys(generated).map(function(a){return{Key: a,Value:generated[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0)/(Object.keys(generated).length||1),
  2282. TestGeneratedDictionaryAverage2 : Object.keys(x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {})).map(function(a){return{Key: a,Value:x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {})[a]};}).map(function(a){return a.Value;}).reduce(function(a, b) { return a + b; }, 0)/(Object.keys(x.Elements.reduce(function(_obj, _cur) {_obj[(function(a){return a.Name;})(_cur)] = (function(a){return a.Decimal;})(_cur);return _obj;}, {})).length||1),
  2283. TestGeneratedDictionaryDirectAccess1 : Object.keys(generated),
  2284. TestGeneratedDictionaryDirectAccess2 : Object.keys(generated).map(function(a){return generated[a];}),
  2285. TestGeneratedDictionaryDirectAccess3 : Object.keys(generated).length,
  2286. TestList1 : elements.reduce(function(a, b) { return a + b; }, 0),
  2287. TestList2 : x.Elements.map(function(a){return a.Decimal;}).reduce(function(a, b) { return a + b; }, 0),
  2288. TestList3 : x.Elements.map(function(a){return a.Decimal;}).reduce(function(a, b) { return a + b; }, 0),
  2289. TestList4 : x.Elements.map(function(a){return a.Decimal;}).reduce(function(a, b) { return a + b; }, 0)/(x.Elements.length||1),
  2290. TestList5 : x.Elements.map(function(a){return a.Decimal;}).reduce(function(a, b) { return a + b; }, 0)/(x.Elements.map((function(a){return a.Decimal;})).length||1)
  2291. };
  2292. };
  2293. ";
  2294. _engine.Execute(program);
  2295. var result1 = (ObjectInstance) _engine.Evaluate("output(x1)");
  2296. var result2 = (ObjectInstance) _engine.Evaluate("output(x2)");
  2297. Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum1")));
  2298. Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum2")));
  2299. Assert.Equal(9, TypeConverter.ToNumber(result1.Get("TestDictionarySum3")));
  2300. Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage1")));
  2301. Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage2")));
  2302. Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryAverage3")));
  2303. Assert.Equal(3, TypeConverter.ToNumber(result1.Get("TestDictionaryFunc1")));
  2304. Assert.Equal(1, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionary3")));
  2305. Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionarySum1")));
  2306. Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionarySum2")));
  2307. Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryAverage1")));
  2308. Assert.Equal(3.5, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryAverage2")));
  2309. Assert.Equal(1, TypeConverter.ToNumber(result1.Get("TestGeneratedDictionaryDirectAccess3")));
  2310. Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList1")));
  2311. Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList2")));
  2312. Assert.Equal(6.7, TypeConverter.ToNumber(result1.Get("TestList3")));
  2313. Assert.Equal(3.35, TypeConverter.ToNumber(result1.Get("TestList4")));
  2314. Assert.Equal(3.35, TypeConverter.ToNumber(result1.Get("TestList5")));
  2315. Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum1")));
  2316. Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum2")));
  2317. Assert.Equal(6, TypeConverter.ToNumber(result2.Get("TestDictionarySum3")));
  2318. Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage1")));
  2319. Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage2")));
  2320. Assert.Equal(2, TypeConverter.ToNumber(result2.Get("TestDictionaryAverage3")));
  2321. }
  2322. [Fact]
  2323. public void ShouldBeAbleToSpreadArrayLiteralsAndFunctionParameters()
  2324. {
  2325. RunTest(@"
  2326. function concat(x, a, b) {
  2327. x += a;
  2328. x += b;
  2329. return x;
  2330. }
  2331. var s = [...'abc'];
  2332. var c = concat(1, ...'ab');
  2333. var arr1 = [1, 2];
  2334. var arr2 = [3, 4 ];
  2335. var r = [...arr2, ...arr1];
  2336. ");
  2337. var arrayInstance = (ArrayInstance) _engine.GetValue("r");
  2338. Assert.Equal(arrayInstance[0], 3);
  2339. Assert.Equal(arrayInstance[1], 4);
  2340. Assert.Equal(arrayInstance[2], 1);
  2341. Assert.Equal(arrayInstance[3], 2);
  2342. arrayInstance = (ArrayInstance) _engine.GetValue("s");
  2343. Assert.Equal(arrayInstance[0], 'a');
  2344. Assert.Equal(arrayInstance[1], 'b');
  2345. Assert.Equal(arrayInstance[2], 'c');
  2346. var c = _engine.GetValue("c").ToString();
  2347. Assert.Equal("1ab", c);
  2348. }
  2349. [Fact]
  2350. public void ShouldSupportDefaultsInFunctionParameters()
  2351. {
  2352. RunTest(@"
  2353. function f(x, y=12) {
  2354. // y is 12 if not passed (or passed as undefined)
  2355. return x + y;
  2356. }
  2357. ");
  2358. var function = _engine.GetValue("f");
  2359. var result = _engine.Invoke(function, 3).ToString();
  2360. Assert.Equal("15", result);
  2361. result = _engine.Invoke(function, 3, JsValue.Undefined).ToString();
  2362. Assert.Equal("15", result);
  2363. }
  2364. [Fact]
  2365. public void ShouldReportErrorForInvalidJson()
  2366. {
  2367. var engine = new Engine();
  2368. var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate("JSON.parse('[01]')"));
  2369. Assert.Equal("Unexpected token '1' in JSON at position 2", ex.Message);
  2370. var voidCompletion = engine.Evaluate("try { JSON.parse('01') } catch (e) {}");
  2371. Assert.Equal(JsValue.Undefined, voidCompletion);
  2372. }
  2373. [Fact]
  2374. public void ShouldParseAnonymousToTypeObject()
  2375. {
  2376. var obj = new Wrapper();
  2377. var engine = new Engine()
  2378. .SetValue("x", obj);
  2379. var js = @"
  2380. x.test = {
  2381. name: 'Testificate',
  2382. init (a, b) {
  2383. return a + b
  2384. }
  2385. }";
  2386. engine.Execute(js);
  2387. Assert.Equal("Testificate", obj.Test.Name);
  2388. Assert.Equal(5, obj.Test.Init(2, 3));
  2389. }
  2390. [Fact]
  2391. public void ShouldOverrideDefaultTypeConverter()
  2392. {
  2393. var engine = new Engine(options => options
  2394. .SetTypeConverter(e => new TestTypeConverter())
  2395. );
  2396. Assert.IsType<TestTypeConverter>(engine.TypeConverter);
  2397. engine.SetValue("x", new Testificate());
  2398. Assert.Throws<JavaScriptException>(() => engine.Evaluate("c.Name"));
  2399. }
  2400. [Fact]
  2401. public void ShouldAllowDollarPrefixForProperties()
  2402. {
  2403. _engine.SetValue("str", "Hello");
  2404. _engine.Evaluate("equal(undefined, str.$ref);");
  2405. _engine.Evaluate("equal(undefined, str.ref);");
  2406. _engine.Evaluate("equal(undefined, str.$foo);");
  2407. _engine.Evaluate("equal(undefined, str.foo);");
  2408. _engine.Evaluate("equal(undefined, str['$foo']);");
  2409. _engine.Evaluate("equal(undefined, str['foo']);");
  2410. _engine.Evaluate("equal(false, str.hasOwnProperty('$foo'));");
  2411. _engine.Evaluate("equal(false, str.hasOwnProperty('foo'));");
  2412. }
  2413. [Fact]
  2414. public void ShouldProvideEngineForOptionsAsOverload()
  2415. {
  2416. new Engine((e, options) =>
  2417. {
  2418. Assert.IsType<Engine>(e);
  2419. options
  2420. .AddObjectConverter(new TestObjectConverter())
  2421. .AddObjectConverter<TestObjectConverter>();
  2422. })
  2423. .SetValue("a", 1);
  2424. }
  2425. [Fact]
  2426. public void ShouldReuseOptions()
  2427. {
  2428. var options = new Options().Configure(e => e.SetValue("x", 1));
  2429. var engine1 = new Engine(options);
  2430. var engine2 = new Engine(options);
  2431. Assert.Equal(1, Convert.ToInt32(engine1.GetValue("x").ToObject()));
  2432. Assert.Equal(1, Convert.ToInt32(engine2.GetValue("x").ToObject()));
  2433. }
  2434. [Fact]
  2435. public void RecursiveCallStack()
  2436. {
  2437. var engine = new Engine();
  2438. Func<string, object> evaluateCode = code => engine.Evaluate(code);
  2439. var evaluateCodeValue = JsValue.FromObject(engine, evaluateCode);
  2440. engine.SetValue("evaluateCode", evaluateCodeValue);
  2441. var result = (int) engine.Evaluate(@"evaluateCode('678 + 711')").AsNumber();
  2442. Assert.Equal(1389, result);
  2443. }
  2444. [Fact]
  2445. public void MemberExpressionInObjectProperty()
  2446. {
  2447. var engine = new Engine();
  2448. dynamic result = engine.Evaluate(@"
  2449. const colorMap = {
  2450. Red: ""red"",
  2451. Orange: ""orange"",
  2452. White: ""white"",
  2453. };
  2454. Object
  2455. .keys(colorMap)
  2456. .reduce((agg, next) => {
  2457. return {...agg, ...{ [colorMap[next]]: next } };
  2458. },
  2459. {});
  2460. ")
  2461. .ToObject();
  2462. Assert.Equal("Red", result.red);
  2463. Assert.Equal("Orange", result.orange);
  2464. Assert.Equal("White", result.white);
  2465. }
  2466. [Fact]
  2467. public void TypeofShouldEvaluateOnce()
  2468. {
  2469. var engine = new Engine();
  2470. var result = engine.Evaluate(@"
  2471. let res = 0;
  2472. const fn = () => res++;
  2473. typeof fn();
  2474. res;
  2475. ")
  2476. .AsNumber();
  2477. Assert.Equal(1, result);
  2478. }
  2479. [Fact]
  2480. public void ClassDeclarationHoisting()
  2481. {
  2482. var ex = Assert.Throws<JavaScriptException>(() => _engine.Evaluate("typeof MyClass; class MyClass {}"));
  2483. Assert.Equal("Cannot access 'MyClass' before initialization", ex.Message);
  2484. }
  2485. [Fact]
  2486. public void ShouldObeyScriptLevelStrictModeInFunctions()
  2487. {
  2488. var engine = new Engine();
  2489. const string source = "'use strict'; var x = () => { delete Boolean.prototype; }; x();";
  2490. var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(source));
  2491. Assert.Equal("Cannot delete property 'prototype' of function Boolean() { [native code] }", ex.Message);
  2492. const string source2 = "'use strict'; delete foobar;";
  2493. ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(source2));
  2494. Assert.Equal("Delete of an unqualified identifier in strict mode.", ex.Message);
  2495. }
  2496. [Fact]
  2497. public void ShouldSupportThisInSubclass()
  2498. {
  2499. var engine = new Engine();
  2500. var script = "class MyClass1 { } class MyClass2 extends MyClass1 { constructor() { } } const x = new MyClass2();";
  2501. var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate(script));
  2502. Assert.Equal("Must call super constructor in derived class before accessing 'this' or returning from derived constructor", ex.Message);
  2503. }
  2504. [Fact]
  2505. public void ShouldGetZeroPrefixedNumericKeys()
  2506. {
  2507. var engine = new Engine();
  2508. engine.Evaluate("const testObj = { '02100' : true };");
  2509. Assert.Equal(1, engine.Evaluate("Object.keys(testObj).length;").AsNumber());
  2510. Assert.Equal("[\"02100\"]", engine.Evaluate("JSON.stringify(Object.getOwnPropertyNames(testObj));").AsString());
  2511. }
  2512. [Fact]
  2513. public void ShouldAllowOptionalChainingForMemberCall()
  2514. {
  2515. var engine = new Engine();
  2516. const string Script = @"
  2517. const adventurer = { name: 'Alice', cat: { name: 'Dinah' } };
  2518. const dogName = adventurer.dog?.name;
  2519. const methodResult = adventurer.someNonExistentMethod?.();
  2520. return [ dogName, methodResult ];
  2521. ";
  2522. var array = engine.Evaluate(Script).AsArray();
  2523. Assert.Equal(2L, array.Length);
  2524. Assert.True(array[0].IsUndefined());
  2525. Assert.True(array[1].IsUndefined());
  2526. }
  2527. [Fact]
  2528. public void CanDisableCompilation()
  2529. {
  2530. var engine = new Engine(options =>
  2531. {
  2532. options.DisableStringCompilation();
  2533. });
  2534. const string ExpectedExceptionMessage = "String compilation has been disabled in engine options";
  2535. var ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate("eval('1+1');"));
  2536. Assert.Equal(ExpectedExceptionMessage, ex.Message);
  2537. ex = Assert.Throws<JavaScriptException>(() => engine.Evaluate("new Function('1+1');"));
  2538. Assert.Equal(ExpectedExceptionMessage, ex.Message);
  2539. }
  2540. [Fact]
  2541. public void ExecuteShouldTriggerBeforeEvaluateEvent()
  2542. {
  2543. TestBeforeEvaluateEvent(
  2544. (engine, code) => engine.Execute(code),
  2545. expectedSource: "<anonymous>"
  2546. );
  2547. }
  2548. [Fact]
  2549. public void ExecuteWithSourceShouldTriggerBeforeEvaluateEvent()
  2550. {
  2551. TestBeforeEvaluateEvent(
  2552. (engine, code) => engine.Execute(code, "mysource"),
  2553. expectedSource: "mysource"
  2554. );
  2555. }
  2556. [Fact]
  2557. public void ExecuteWithParserOptionsShouldTriggerBeforeEvaluateEvent()
  2558. {
  2559. TestBeforeEvaluateEvent(
  2560. (engine, code) => engine.Execute(code, ParserOptions.Default),
  2561. expectedSource: "<anonymous>"
  2562. );
  2563. }
  2564. [Fact]
  2565. public void ExecuteWithSourceAndParserOptionsShouldTriggerBeforeEvaluateEvent()
  2566. {
  2567. TestBeforeEvaluateEvent(
  2568. (engine, code) => engine.Execute(code, "mysource", ParserOptions.Default),
  2569. expectedSource: "mysource"
  2570. );
  2571. }
  2572. [Fact]
  2573. public void EvaluateShouldTriggerBeforeEvaluateEvent()
  2574. {
  2575. TestBeforeEvaluateEvent(
  2576. (engine, code) => engine.Evaluate(code),
  2577. expectedSource: "<anonymous>"
  2578. );
  2579. }
  2580. [Fact]
  2581. public void EvaluateWithSourceShouldTriggerBeforeEvaluateEvent()
  2582. {
  2583. TestBeforeEvaluateEvent(
  2584. (engine, code) => engine.Evaluate(code, "mysource"),
  2585. expectedSource: "mysource"
  2586. );
  2587. }
  2588. [Fact]
  2589. public void EvaluateWithParserOptionsShouldTriggerBeforeEvaluateEvent()
  2590. {
  2591. TestBeforeEvaluateEvent(
  2592. (engine, code) => engine.Evaluate(code, ParserOptions.Default),
  2593. expectedSource: "<anonymous>"
  2594. );
  2595. }
  2596. [Fact]
  2597. public void EvaluateWithSourceAndParserOptionsShouldTriggerBeforeEvaluateEvent()
  2598. {
  2599. TestBeforeEvaluateEvent(
  2600. (engine, code) => engine.Evaluate(code, "mysource", ParserOptions.Default),
  2601. expectedSource: "mysource"
  2602. );
  2603. }
  2604. [Fact]
  2605. public void ImportModuleShouldTriggerBeforeEvaluateEvents()
  2606. {
  2607. var engine = new Engine();
  2608. const string module1 = "import dummy from 'module2';";
  2609. const string module2 = "export default 'dummy';";
  2610. var beforeEvaluateTriggeredCount = 0;
  2611. engine.Debugger.BeforeEvaluate += (sender, ast) =>
  2612. {
  2613. beforeEvaluateTriggeredCount++;
  2614. Assert.Equal(engine, sender);
  2615. switch (beforeEvaluateTriggeredCount)
  2616. {
  2617. case 1:
  2618. Assert.Equal("module1", ast.Location.Source);
  2619. Assert.Collection(ast.Body,
  2620. node => Assert.IsType<ImportDeclaration>(node)
  2621. );
  2622. break;
  2623. case 2:
  2624. Assert.Equal("module2", ast.Location.Source);
  2625. Assert.Collection(ast.Body,
  2626. node => Assert.IsType<ExportDefaultDeclaration>(node)
  2627. );
  2628. break;
  2629. }
  2630. };
  2631. engine.Modules.Add("module1", module1);
  2632. engine.Modules.Add("module2", module2);
  2633. engine.Modules.Import("module1");
  2634. Assert.Equal(2, beforeEvaluateTriggeredCount);
  2635. }
  2636. [Fact]
  2637. public void ShouldConvertJsTypedArraysCorrectly()
  2638. {
  2639. var engine = new Engine();
  2640. var float32 = new float [] { 42f, 23 };
  2641. engine.SetValue("float32", float32);
  2642. engine.SetValue("testFloat32Array", new Action<float[]>(v => Assert.Equal(v, float32)));
  2643. engine.Evaluate(@"
  2644. testFloat32Array(new Float32Array(float32));
  2645. ");
  2646. }
  2647. private static void TestBeforeEvaluateEvent(Action<Engine, string> call, string expectedSource)
  2648. {
  2649. var engine = new Engine();
  2650. const string script = "'dummy';";
  2651. var beforeEvaluateTriggered = false;
  2652. engine.Debugger.BeforeEvaluate += (sender, ast) =>
  2653. {
  2654. beforeEvaluateTriggered = true;
  2655. Assert.Equal(engine, sender);
  2656. Assert.Equal(expectedSource, ast.Location.Source);
  2657. Assert.Collection(ast.Body, node => Assert.True(TestHelpers.IsLiteral(node, "dummy")));
  2658. };
  2659. call(engine, script);
  2660. Assert.True(beforeEvaluateTriggered);
  2661. }
  2662. private class Wrapper
  2663. {
  2664. public Testificate Test { get; set; }
  2665. }
  2666. private class Testificate
  2667. {
  2668. public string Name { get; set; }
  2669. public Func<int, int, int> Init { get; set; }
  2670. }
  2671. private class TestObjectConverter : Jint.Runtime.Interop.IObjectConverter
  2672. {
  2673. public bool TryConvert(Engine engine, object value, out JsValue result)
  2674. {
  2675. throw new NotImplementedException();
  2676. }
  2677. }
  2678. private class TestTypeConverter : Jint.Runtime.Interop.ITypeConverter
  2679. {
  2680. public object Convert(object value, Type type, IFormatProvider formatProvider)
  2681. {
  2682. throw new NotImplementedException();
  2683. }
  2684. public bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
  2685. {
  2686. throw new NotImplementedException();
  2687. }
  2688. }
  2689. }
  2690. }