@@ -466,7 +466,7 @@ NO_INLINE JsVar *jspeFunctionDefinition(bool parseNamedFunction) {
466
466
// Parse the actual function block
467
467
jspeFunctionDefinitionInternal (funcVar , false);
468
468
469
- // if we had a function name, add it to the end
469
+ // if we had a function name, add it to the end (if we don't it gets confused with arguments)
470
470
if (funcVar && functionInternalName )
471
471
jsvObjectSetChildAndUnLock (funcVar , JSPARSE_FUNCTION_NAME_NAME , functionInternalName );
472
472
@@ -1191,7 +1191,20 @@ NO_INLINE JsVar *jspeFactorFunctionCall() {
1191
1191
}
1192
1192
1193
1193
JsVar * parent = 0 ;
1194
+ #ifndef SAVE_ON_FLASH
1195
+ bool wasSuper = lex -> tk == LEX_R_SUPER ;
1196
+ #endif
1194
1197
JsVar * a = jspeFactorMember (jspeFactor (), & parent );
1198
+ #ifndef SAVE_ON_FLASH
1199
+ if (wasSuper ) {
1200
+ /* if this was 'super.something' then we need
1201
+ * to overwrite the parent, because it'll be
1202
+ * set to the prototype otherwise.
1203
+ */
1204
+ jsvUnLock (parent );
1205
+ parent = jsvLockAgainSafe (execInfo .thisVar );
1206
+ }
1207
+ #endif
1195
1208
1196
1209
while ((lex -> tk == '(' || (isConstructor && JSP_SHOULD_EXECUTE )) && !jspIsInterrupted ()) {
1197
1210
JsVar * funcName = a ;
@@ -1481,6 +1494,73 @@ NO_INLINE JsVar *jspeExpressionOrArrowFunction() {
1481
1494
return a ;
1482
1495
}
1483
1496
}
1497
+
1498
+ /// Parse an ES6 class, expects LEX_R_CLASS already parsed
1499
+ NO_INLINE JsVar * jspeClassDefinition (bool parseNamedClass ) {
1500
+ JsVar * classFunction = 0 ;
1501
+ JsVar * classPrototype = 0 ;
1502
+ JsVar * classInternalName = 0 ;
1503
+
1504
+ bool actuallyCreateClass = JSP_SHOULD_EXECUTE ;
1505
+ if (actuallyCreateClass )
1506
+ classFunction = jsvNewWithFlags (JSV_FUNCTION );
1507
+
1508
+ if (parseNamedClass && lex -> tk == LEX_ID ) {
1509
+ if (classFunction )
1510
+ classInternalName = jslGetTokenValueAsVar (lex );
1511
+ JSP_ASSERT_MATCH (LEX_ID );
1512
+ }
1513
+ if (classFunction ) {
1514
+ JsVar * prototypeName = jsvFindChildFromString (classFunction , JSPARSE_PROTOTYPE_VAR , true);
1515
+ jspEnsureIsPrototype (classFunction , prototypeName ); // make sure it's an object
1516
+ classPrototype = jsvSkipName (prototypeName );
1517
+ jsvUnLock (prototypeName );
1518
+ }
1519
+ if (lex -> tk == LEX_R_EXTENDS ) {
1520
+ JSP_ASSERT_MATCH (LEX_R_EXTENDS );
1521
+ JsVar * extendsFrom = actuallyCreateClass ? jsvSkipNameAndUnLock (jspGetNamedVariable (jslGetTokenValueAsString (lex ))) : 0 ;
1522
+ JSP_MATCH_WITH_CLEANUP_AND_RETURN (LEX_ID ,jsvUnLock4 (extendsFrom ,classFunction ,classInternalName ,classPrototype ),0 );
1523
+ if (classPrototype ) {
1524
+ if (jsvIsFunction (extendsFrom )) {
1525
+ jsvObjectSetChild (classPrototype , JSPARSE_INHERITS_VAR , extendsFrom );
1526
+ // link in default constructor if ours isn't supplied
1527
+ jsvObjectSetChildAndUnLock (classFunction , JSPARSE_FUNCTION_CODE_NAME , jsvNewFromString ("if(this.__proto__.__proto__)this.__proto__.__proto__.apply(this,arguments)" ));
1528
+ } else
1529
+ jsExceptionHere (JSET_SYNTAXERROR , "'extends' argument should be a function, got %t" , extendsFrom );
1530
+ }
1531
+ jsvUnLock (extendsFrom );
1532
+ }
1533
+ JSP_MATCH_WITH_CLEANUP_AND_RETURN ('{' ,jsvUnLock3 (classFunction ,classInternalName ,classPrototype ),0 );
1534
+
1535
+ while ((lex -> tk == LEX_ID || lex -> tk == LEX_R_STATIC ) && !jspIsInterrupted ()) {
1536
+ bool isStatic = lex -> tk == LEX_R_STATIC ;
1537
+ if (isStatic ) JSP_ASSERT_MATCH (LEX_R_STATIC );
1538
+
1539
+ JsVar * funcName = jslGetTokenValueAsVar (lex );
1540
+ JSP_MATCH_WITH_CLEANUP_AND_RETURN (LEX_ID ,jsvUnLock3 (classFunction ,classInternalName ,classPrototype ),0 );
1541
+ JsVar * method = jspeFunctionDefinition (false);
1542
+ if (classFunction && classPrototype ) {
1543
+ if (jsvIsStringEqual (funcName , "get" ) || jsvIsStringEqual (funcName , "set" )) {
1544
+ jsExceptionHere (JSET_SYNTAXERROR , "'get' and 'set' and not supported in Espruino" );
1545
+ } else if (jsvIsStringEqual (funcName , "constructor" )) {
1546
+ jswrap_function_replaceWith (classFunction , method );
1547
+ } else {
1548
+ funcName = jsvMakeIntoVariableName (funcName , 0 );
1549
+ jsvSetValueOfName (funcName , method );
1550
+ jsvAddName (isStatic ? classFunction : classPrototype , funcName );
1551
+ }
1552
+ }
1553
+ jsvUnLock2 (method ,funcName );
1554
+ }
1555
+ jsvUnLock (classPrototype );
1556
+ // If we had a name, add it to the end (or it gets confused with the constructor arguments)
1557
+ if (classInternalName )
1558
+ jsvObjectSetChildAndUnLock (classFunction , JSPARSE_FUNCTION_NAME_NAME , classInternalName );
1559
+
1560
+ JSP_MATCH_WITH_CLEANUP_AND_RETURN ('}' ,jsvUnLock (classFunction ),0 );
1561
+ return classFunction ;
1562
+ }
1563
+
1484
1564
#endif
1485
1565
1486
1566
NO_INLINE JsVar * jspeFactor () {
@@ -1580,6 +1660,49 @@ NO_INLINE JsVar *jspeFactor() {
1580
1660
if (!jspCheckStackPosition ()) return 0 ;
1581
1661
JSP_ASSERT_MATCH (LEX_R_FUNCTION );
1582
1662
return jspeFunctionDefinition (true);
1663
+ #ifndef SAVE_ON_FLASH
1664
+ } else if (lex -> tk == LEX_R_CLASS ) {
1665
+ if (!jspCheckStackPosition ()) return 0 ;
1666
+ JSP_ASSERT_MATCH (LEX_R_CLASS );
1667
+ return jspeClassDefinition (true);
1668
+ } else if (lex -> tk == LEX_R_SUPER ) {
1669
+ JSP_ASSERT_MATCH (LEX_R_SUPER );
1670
+ /* This is kind of nasty, since super appears to do
1671
+ three different things.
1672
+
1673
+ * In the constructor it references the extended class's constructor
1674
+ * in a method it references the constructor's prototype.
1675
+ * in a static method it references the extended class's constructor (but this is different)
1676
+ */
1677
+
1678
+ if (jsvIsObject (execInfo .thisVar )) {
1679
+ // 'this' is an object - must be calling a normal method
1680
+ JsVar * proto1 = jsvObjectGetChild (execInfo .thisVar , JSPARSE_INHERITS_VAR , 0 ); // if we're in a method, get __proto__ first
1681
+ JsVar * proto2 = jsvIsObject (proto1 ) ? jsvObjectGetChild (proto1 , JSPARSE_INHERITS_VAR , 0 ) : 0 ; // still in method, get __proto__.__proto__
1682
+ jsvUnLock (proto1 );
1683
+ if (!proto2 ) {
1684
+ jsExceptionHere (JSET_SYNTAXERROR , "Calling 'super' outside of class" );
1685
+ return 0 ;
1686
+ }
1687
+ if (lex -> tk == '(' ) return proto2 ; // eg. used in a constructor
1688
+ // But if we're doing something else - eg '.' or '[' then it needs to reference the prototype
1689
+ JsVar * proto3 = jsvIsFunction (proto2 ) ? jsvObjectGetChild (proto2 , JSPARSE_PROTOTYPE_VAR , 0 ) : 0 ;
1690
+ jsvUnLock (proto2 );
1691
+ return proto3 ;
1692
+ } else if (jsvIsFunction (execInfo .thisVar )) {
1693
+ // 'this' is a function - must be calling a static method
1694
+ JsVar * proto1 = jsvObjectGetChild (execInfo .thisVar , JSPARSE_PROTOTYPE_VAR , 0 );
1695
+ JsVar * proto2 = jsvIsObject (proto1 ) ? jsvObjectGetChild (proto1 , JSPARSE_INHERITS_VAR , 0 ) : 0 ;
1696
+ jsvUnLock (proto1 );
1697
+ if (!proto2 ) {
1698
+ jsExceptionHere (JSET_SYNTAXERROR , "Calling 'super' outside of class" );
1699
+ return 0 ;
1700
+ }
1701
+ return proto2 ;
1702
+ }
1703
+ jsExceptionHere (JSET_SYNTAXERROR , "Calling 'super' outside of class" );
1704
+ return 0 ;
1705
+ #endif
1583
1706
} else if (lex -> tk == LEX_R_THIS ) {
1584
1707
JSP_ASSERT_MATCH (LEX_R_THIS );
1585
1708
return jsvLockAgain ( execInfo .thisVar ? execInfo .thisVar : execInfo .root );
@@ -2460,21 +2583,29 @@ NO_INLINE JsVar *jspeStatementThrow() {
2460
2583
return 0 ;
2461
2584
}
2462
2585
2463
- NO_INLINE JsVar * jspeStatementFunctionDecl () {
2586
+ NO_INLINE JsVar * jspeStatementFunctionDecl (bool isClass ) {
2464
2587
JsVar * funcName = 0 ;
2465
2588
JsVar * funcVar ;
2589
+
2590
+ #ifndef SAVE_ON_FLASH
2591
+ JSP_ASSERT_MATCH (isClass ? LEX_R_CLASS : LEX_R_FUNCTION );
2592
+ #else
2466
2593
JSP_ASSERT_MATCH (LEX_R_FUNCTION );
2594
+ #endif
2467
2595
2468
2596
bool actuallyCreateFunction = JSP_SHOULD_EXECUTE ;
2469
2597
if (actuallyCreateFunction ) {
2470
2598
funcName = jsvMakeIntoVariableName (jslGetTokenValueAsVar (lex ), 0 );
2471
2599
if (!funcName ) { // out of memory
2472
- jspSetError (false);
2473
2600
return 0 ;
2474
2601
}
2475
2602
}
2476
2603
JSP_MATCH_WITH_CLEANUP_AND_RETURN (LEX_ID , jsvUnLock (funcName ), 0 );
2604
+ #ifndef SAVE_ON_FLASH
2605
+ funcVar = isClass ? jspeClassDefinition (false) : jspeFunctionDefinition (false);
2606
+ #else
2477
2607
funcVar = jspeFunctionDefinition (false);
2608
+ #endif
2478
2609
if (actuallyCreateFunction ) {
2479
2610
// find a function with the same name (or make one)
2480
2611
// OPT: can Find* use just a JsVar that is a 'name'?
@@ -2520,6 +2651,7 @@ NO_INLINE JsVar *jspeStatement() {
2520
2651
lex -> tk == LEX_R_DELETE ||
2521
2652
lex -> tk == LEX_R_TYPEOF ||
2522
2653
lex -> tk == LEX_R_VOID ||
2654
+ lex -> tk == LEX_R_SUPER ||
2523
2655
lex -> tk == LEX_PLUSPLUS ||
2524
2656
lex -> tk == LEX_MINUSMINUS ||
2525
2657
lex -> tk == '!' ||
@@ -2557,7 +2689,11 @@ NO_INLINE JsVar *jspeStatement() {
2557
2689
} else if (lex -> tk == LEX_R_THROW ) {
2558
2690
return jspeStatementThrow ();
2559
2691
} else if (lex -> tk == LEX_R_FUNCTION ) {
2560
- return jspeStatementFunctionDecl ();
2692
+ return jspeStatementFunctionDecl (false/* function */ );
2693
+ #ifndef SAVE_ON_FLASH
2694
+ } else if (lex -> tk == LEX_R_CLASS ) {
2695
+ return jspeStatementFunctionDecl (true/* class */ );
2696
+ #endif
2561
2697
} else if (lex -> tk == LEX_R_CONTINUE ) {
2562
2698
JSP_ASSERT_MATCH (LEX_R_CONTINUE );
2563
2699
if (JSP_SHOULD_EXECUTE ) {
0 commit comments