pxt-core 13.2.4 → 13.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/built/cli.js +2 -2
- package/built/pxt.js +127 -25
- package/built/pxtcompiler.js +102 -16
- package/built/pxtlib.js +23 -7
- package/built/target.js +1 -1
- package/built/targetlight.js +1 -1
- package/built/web/main.js +1 -1
- package/built/web/pxtapp.js +1 -1
- package/built/web/pxtcompiler.js +1 -1
- package/built/web/pxtembed.js +1 -1
- package/built/web/pxtlib.js +1 -1
- package/built/web/pxtworker.js +1 -1
- package/package.json +1 -1
package/built/cli.js
CHANGED
|
@@ -1859,8 +1859,8 @@ function saveThemeJson(cfg, localDir, packaged) {
|
|
|
1859
1859
|
const url = card.url || card.learnMoreUrl || card.buyUrl || (card.youTubeId && `https://youtu.be/${card.youTubeId}`);
|
|
1860
1860
|
tocmd += ` * [${card.name || card.title}](${url})
|
|
1861
1861
|
`;
|
|
1862
|
-
|
|
1863
|
-
|
|
1862
|
+
for (const term of (card.tags || []).concat(card.searchTerms || []))
|
|
1863
|
+
targetStrings[term] = term;
|
|
1864
1864
|
}));
|
|
1865
1865
|
});
|
|
1866
1866
|
nodeutil.writeFileSync(path.join(docsRoot, "docs/projects/SUMMARY.md"), tocmd, { encoding: "utf8" });
|
package/built/pxt.js
CHANGED
|
@@ -108094,7 +108094,7 @@ var pxt;
|
|
|
108094
108094
|
if (cards && !Array.isArray(cards))
|
|
108095
108095
|
cards = [cards];
|
|
108096
108096
|
if (cards === null || cards === void 0 ? void 0 : cards.length)
|
|
108097
|
-
return cards;
|
|
108097
|
+
return cards.map(normalizeCodeCardStringArrays);
|
|
108098
108098
|
// not json, try parsing as sequence of key,value pairs, with line splits
|
|
108099
108099
|
cards = md.split(/^---$/gm)
|
|
108100
108100
|
.filter(cmd => !!cmd)
|
|
@@ -108120,7 +108120,7 @@ var pxt;
|
|
|
108120
108120
|
})
|
|
108121
108121
|
.filter(cc => !!cc);
|
|
108122
108122
|
if (cards === null || cards === void 0 ? void 0 : cards.length)
|
|
108123
|
-
return cards;
|
|
108123
|
+
return cards.map(normalizeCodeCardStringArrays);
|
|
108124
108124
|
return undefined;
|
|
108125
108125
|
}
|
|
108126
108126
|
gallery_1.parseCodeCards = parseCodeCards;
|
|
@@ -108161,9 +108161,18 @@ var pxt;
|
|
|
108161
108161
|
// legacy JSON format
|
|
108162
108162
|
cards = pxt.Util.jsonTryParse(el.textContent);
|
|
108163
108163
|
}
|
|
108164
|
-
return !!(cards === null || cards === void 0 ? void 0 : cards.length) && cards;
|
|
108164
|
+
return !!(cards === null || cards === void 0 ? void 0 : cards.length) && cards.map(normalizeCodeCardStringArrays);
|
|
108165
108165
|
}
|
|
108166
108166
|
gallery_1.parseCodeCardsHtml = parseCodeCardsHtml;
|
|
108167
|
+
function normalizeCodeCardStringArrays(card) {
|
|
108168
|
+
["tags", "searchTerms"].forEach(field => {
|
|
108169
|
+
const value = card[field];
|
|
108170
|
+
if (typeof value === "string") {
|
|
108171
|
+
card[field] = value.split(',').map(term => term.trim()).filter(term => !!term);
|
|
108172
|
+
}
|
|
108173
|
+
});
|
|
108174
|
+
return card;
|
|
108175
|
+
}
|
|
108167
108176
|
function parseGalleryMardown(md) {
|
|
108168
108177
|
if (!md)
|
|
108169
108178
|
return [];
|
|
@@ -116497,18 +116506,25 @@ var ts;
|
|
|
116497
116506
|
pxt.debug(`Localized block has extra or missing parameters`);
|
|
116498
116507
|
return false;
|
|
116499
116508
|
}
|
|
116509
|
+
if (!!a.thisParameter != !!b.thisParameter
|
|
116510
|
+
|| (a.thisParameter && !isEquivalentParameter(a.thisParameter, b.thisParameter))) {
|
|
116511
|
+
pxt.debug(`Localized block's this parameter shadow block or definition name does not match`);
|
|
116512
|
+
return false;
|
|
116513
|
+
}
|
|
116500
116514
|
for (const aParam of a.parameters) {
|
|
116501
116515
|
const bParam = b.actualNameToParam[aParam.actualName];
|
|
116502
|
-
if (!bParam
|
|
116503
|
-
|| aParam.type != bParam.type
|
|
116504
|
-
|| aParam.shadowBlockId != bParam.shadowBlockId
|
|
116505
|
-
|| aParam.definitionName != bParam.definitionName) {
|
|
116516
|
+
if (!bParam || !isEquivalentParameter(aParam, bParam)) {
|
|
116506
116517
|
pxt.debug(`Parameter ${aParam.actualName} type, shadow block, or definition name does not match after localization`);
|
|
116507
116518
|
return false;
|
|
116508
116519
|
}
|
|
116509
116520
|
}
|
|
116510
116521
|
return true;
|
|
116511
116522
|
}
|
|
116523
|
+
function isEquivalentParameter(a, b) {
|
|
116524
|
+
return a.type == b.type
|
|
116525
|
+
&& a.shadowBlockId == b.shadowBlockId
|
|
116526
|
+
&& a.definitionName == b.definitionName;
|
|
116527
|
+
}
|
|
116512
116528
|
function emptyExtInfo() {
|
|
116513
116529
|
let cs = pxt.appTarget.compileService;
|
|
116514
116530
|
if (!cs)
|
|
@@ -135162,6 +135178,7 @@ var ts;
|
|
|
135162
135178
|
let currUsingContext = null;
|
|
135163
135179
|
let needsUsingInfo = false;
|
|
135164
135180
|
let pendingFunctionDefinitions = [];
|
|
135181
|
+
const loopBlockFunctionDefinitions = {};
|
|
135165
135182
|
currNodeWave++;
|
|
135166
135183
|
if (opts.target.isNative) {
|
|
135167
135184
|
if (!opts.extinfo || !opts.extinfo.hexinfo) {
|
|
@@ -136462,10 +136479,14 @@ ${lbl}: .short 0xffff
|
|
|
136462
136479
|
args.push(irToNode(expr));
|
|
136463
136480
|
}
|
|
136464
136481
|
else {
|
|
136465
|
-
|
|
136466
|
-
|
|
136482
|
+
// Emitted functions apply their own defaults, including on
|
|
136483
|
+
// virtual/interface calls where the implementation may differ.
|
|
136484
|
+
// Shims and helpers may have no emitted parameter prologue.
|
|
136485
|
+
const needsCallerDefault = attrs.helper || attrs.shim;
|
|
136486
|
+
if (needsCallerDefault && !opts.unfetteredInitializers && !isNumericLiteral(prm.initializer)) {
|
|
136487
|
+
userError(9212, lf("only numbers, null, true and false supported as default arguments for shims and helpers"));
|
|
136467
136488
|
}
|
|
136468
|
-
args.push(prm.initializer);
|
|
136489
|
+
args.push(needsCallerDefault ? prm.initializer : irToNode(emitLit(undefined)));
|
|
136469
136490
|
}
|
|
136470
136491
|
}
|
|
136471
136492
|
else {
|
|
@@ -137204,12 +137225,8 @@ ${lbl}: .short 0xffff
|
|
|
137204
137225
|
}
|
|
137205
137226
|
}
|
|
137206
137227
|
}
|
|
137207
|
-
const destructuredParameters = [];
|
|
137208
137228
|
const fieldAssignmentParameters = [];
|
|
137209
137229
|
proc.args = getParameters(node).map((p, i) => {
|
|
137210
|
-
if (p.name.kind === pxtc.SK.ObjectBindingPattern) {
|
|
137211
|
-
destructuredParameters.push(p);
|
|
137212
|
-
}
|
|
137213
137230
|
if (node.kind == pxtc.SK.Constructor && isCtorField(p)) {
|
|
137214
137231
|
fieldAssignmentParameters.push(p);
|
|
137215
137232
|
}
|
|
@@ -137227,7 +137244,34 @@ ${lbl}: .short 0xffff
|
|
|
137227
137244
|
proc.emitExpr(l.storeDirect(tmp));
|
|
137228
137245
|
}
|
|
137229
137246
|
});
|
|
137230
|
-
|
|
137247
|
+
// Arity wrappers pad omitted arguments with undefined. Keep defaults in
|
|
137248
|
+
// the shared body so direct, virtual, interface and action calls all run
|
|
137249
|
+
// them, including native entries which can skip the arity wrapper.
|
|
137250
|
+
const attrs = parseComments(node);
|
|
137251
|
+
proc.args.forEach(l => {
|
|
137252
|
+
const parameter = l.def;
|
|
137253
|
+
// Simulator shim dummies must match the native caller-side behavior.
|
|
137254
|
+
if (parameter.initializer && !attrs.shim && !attrs.helper) {
|
|
137255
|
+
const supplied = proc.mkLabel("defaultarg");
|
|
137256
|
+
if (isThumb()) {
|
|
137257
|
+
// Tagged undefined is 0, so the guard is a bare
|
|
137258
|
+
// compare against zero rather than a runtime call.
|
|
137259
|
+
proc.emitJmp(supplied, l.load(), pxtc.ir.JmpMode.IfNotZero);
|
|
137260
|
+
}
|
|
137261
|
+
else {
|
|
137262
|
+
// The simulator holds real JS values; a raw zero test
|
|
137263
|
+
// would treat a supplied 0/false/null as omitted.
|
|
137264
|
+
proc.emitJmpZ(supplied, pxtc.ir.rtcall("pxt::eqq_bool", [l.load(), emitLit(undefined)]));
|
|
137265
|
+
}
|
|
137266
|
+
proc.emitExpr(l.storeByRef(emitEscapedExpression(parameter.initializer, "initializer")));
|
|
137267
|
+
proc.emitLbl(supplied);
|
|
137268
|
+
proc.stackEmpty();
|
|
137269
|
+
}
|
|
137270
|
+
if (parameter.name.kind === pxtc.SK.ObjectBindingPattern) {
|
|
137271
|
+
// Destructure the resolved argument, not the initializer again.
|
|
137272
|
+
emitVarOrParam(parameter, l.load(), typeOf(parameter));
|
|
137273
|
+
}
|
|
137274
|
+
});
|
|
137231
137275
|
// for constructor(public foo:number) generate this.foo = foo;
|
|
137232
137276
|
for (let p of fieldAssignmentParameters) {
|
|
137233
137277
|
let idx = fieldIndexCore(proc.classInfo, getFieldInfo(proc.classInfo, getName(p)), false);
|
|
@@ -137270,11 +137314,25 @@ ${lbl}: .short 0xffff
|
|
|
137270
137314
|
if (info.capturedVars.length &&
|
|
137271
137315
|
info.usedBeforeDecl &&
|
|
137272
137316
|
node.kind == pxtc.SK.FunctionDeclaration && !bin.finalPass) {
|
|
137273
|
-
|
|
137274
|
-
|
|
137275
|
-
|
|
137276
|
-
|
|
137277
|
-
|
|
137317
|
+
// Synthetic captures (such as thisParameter) are available before
|
|
137318
|
+
// source declarations, but may not have a source position.
|
|
137319
|
+
const capturePos = (capture) => capture.pos == null ? -1 : capture.pos;
|
|
137320
|
+
info.capturedVars.sort((a, b) => capturePos(b) - capturePos(a));
|
|
137321
|
+
const scope = node.parent;
|
|
137322
|
+
if (scope.kind === pxtc.SK.Block && inLoop(scope) && info.capturedVars.every(capture => capturePos(capture) < scope.pos)) {
|
|
137323
|
+
// All captures precede this loop block. Defining the function at
|
|
137324
|
+
// the last captured variable would hoist it out of the iteration.
|
|
137325
|
+
const id = getNodeId(scope);
|
|
137326
|
+
const definitions = loopBlockFunctionDefinitions[id] || (loopBlockFunctionDefinitions[id] = []);
|
|
137327
|
+
if (definitions.indexOf(node) < 0)
|
|
137328
|
+
definitions.push(node);
|
|
137329
|
+
}
|
|
137330
|
+
else {
|
|
137331
|
+
const vinfo = getVarInfo(info.capturedVars[0]);
|
|
137332
|
+
if (!vinfo.functionsToDefine)
|
|
137333
|
+
vinfo.functionsToDefine = [];
|
|
137334
|
+
vinfo.functionsToDefine.push(node);
|
|
137335
|
+
}
|
|
137278
137336
|
}
|
|
137279
137337
|
// nothing should be on work list in final pass - everything should be already marked as used
|
|
137280
137338
|
pxtc.assert(!bin.finalPass || usedWorkList.length == 0, "!bin.finalPass || usedWorkList.length == 0");
|
|
@@ -138231,6 +138289,11 @@ ${lbl}: .short 0xffff
|
|
|
138231
138289
|
function emitSpreadElementExpression(node) { }
|
|
138232
138290
|
function emitYieldExpression(node) { }
|
|
138233
138291
|
function emitBlock(node) {
|
|
138292
|
+
const definitions = bin.finalPass && loopBlockFunctionDefinitions[getNodeId(node)];
|
|
138293
|
+
if (definitions) {
|
|
138294
|
+
pxtc.U.pushRange(pendingFunctionDefinitions, definitions);
|
|
138295
|
+
flushHoistedFunctionDefinitions();
|
|
138296
|
+
}
|
|
138234
138297
|
node.statements.forEach(emit);
|
|
138235
138298
|
}
|
|
138236
138299
|
function checkForLetOrConst(declList) {
|
|
@@ -138416,16 +138479,45 @@ ${lbl}: .short 0xffff
|
|
|
138416
138479
|
proc.emitExpr(v);
|
|
138417
138480
|
proc.stackEmpty();
|
|
138418
138481
|
}
|
|
138482
|
+
function emitForIterationBindings(declList) {
|
|
138483
|
+
// Only `let` loop headers create copied bindings. `const` and variables
|
|
138484
|
+
// declared outside the loop retain their existing binding semantics.
|
|
138485
|
+
if (!declList || !(declList.flags & ts.NodeFlags.Let))
|
|
138486
|
+
return;
|
|
138487
|
+
const copyBinding = (decl) => {
|
|
138488
|
+
if (decl.name.kind === pxtc.SK.ObjectBindingPattern || decl.name.kind === pxtc.SK.ArrayBindingPattern) {
|
|
138489
|
+
decl.name.elements.forEach(element => {
|
|
138490
|
+
if (element.kind === pxtc.SK.BindingElement)
|
|
138491
|
+
copyBinding(element);
|
|
138492
|
+
});
|
|
138493
|
+
return;
|
|
138494
|
+
}
|
|
138495
|
+
const loc = proc.localIndex(decl);
|
|
138496
|
+
if (!loc || !loc.isByRefLocal())
|
|
138497
|
+
return;
|
|
138498
|
+
// Populate the new box before replacing the old cell. Closures already
|
|
138499
|
+
// created keep the old box; sibling closures in this iteration share the new one.
|
|
138500
|
+
const next = pxtc.ir.shared(pxtc.ir.rtcall("pxtrt::mklocRef", []));
|
|
138501
|
+
proc.emitExpr(pxtc.ir.rtcall("pxtrt::stlocRef", [next, loc.load()]));
|
|
138502
|
+
proc.emitExpr(loc.storeDirect(next));
|
|
138503
|
+
proc.stackEmpty();
|
|
138504
|
+
};
|
|
138505
|
+
declList.declarations.forEach(copyBinding);
|
|
138506
|
+
}
|
|
138419
138507
|
function emitForStatement(node) {
|
|
138508
|
+
let declList;
|
|
138420
138509
|
if (node.initializer && node.initializer.kind == pxtc.SK.VariableDeclarationList) {
|
|
138421
|
-
|
|
138422
|
-
|
|
138510
|
+
declList = node.initializer;
|
|
138511
|
+
checkForLetOrConst(declList);
|
|
138512
|
+
declList.declarations.forEach(emit);
|
|
138423
138513
|
}
|
|
138424
138514
|
else {
|
|
138425
138515
|
emitExprAsStmt(node.initializer);
|
|
138426
138516
|
}
|
|
138427
138517
|
emitBrk(node);
|
|
138428
138518
|
let l = getLabels(node);
|
|
138519
|
+
// Initializer closures keep their own binding, separate from the first condition/body.
|
|
138520
|
+
emitForIterationBindings(declList);
|
|
138429
138521
|
proc.emitLblDirect(l.fortop);
|
|
138430
138522
|
if (node.condition) {
|
|
138431
138523
|
emitBrk(node.condition);
|
|
@@ -138433,6 +138525,8 @@ ${lbl}: .short 0xffff
|
|
|
138433
138525
|
}
|
|
138434
138526
|
emit(node.statement);
|
|
138435
138527
|
proc.emitLblDirect(l.cont);
|
|
138528
|
+
// A continue must also create the next binding BEFORE evaluating the incrementor.
|
|
138529
|
+
emitForIterationBindings(declList);
|
|
138436
138530
|
emitExprAsStmt(node.incrementor);
|
|
138437
138531
|
proc.emitJmp(l.fortop);
|
|
138438
138532
|
proc.emitLblDirect(l.brk);
|
|
@@ -138492,6 +138586,11 @@ ${lbl}: .short 0xffff
|
|
|
138492
138586
|
};
|
|
138493
138587
|
// c = a[i]
|
|
138494
138588
|
if (iterVar) {
|
|
138589
|
+
// A mutable captured for-of variable needs a fresh box for each element,
|
|
138590
|
+
// rather than overwriting the box retained by closures from earlier elements.
|
|
138591
|
+
if (iterVar.isByRefLocal()) {
|
|
138592
|
+
proc.emitExpr(iterVar.storeDirect(pxtc.ir.rtcall("pxtrt::mklocRef", [])));
|
|
138593
|
+
}
|
|
138495
138594
|
proc.emitExpr(iterVar.storeByRef(pxtc.ir.rtcall(indexer, [collectionVar.loadCore(), toInt(intVarIter.loadCore())])));
|
|
138496
138595
|
emitBrk(node.initializer);
|
|
138497
138596
|
}
|
|
@@ -138605,7 +138704,10 @@ ${lbl}: .short 0xffff
|
|
|
138605
138704
|
function emitLabeledStatement(node) {
|
|
138606
138705
|
let l = getLabels(node.statement);
|
|
138607
138706
|
emit(node.statement);
|
|
138608
|
-
|
|
138707
|
+
// Loops and switches define their own break target. Emitting it twice
|
|
138708
|
+
// is tolerated by simjs but rejected by the native assembler.
|
|
138709
|
+
if (!ts.isIterationStatement(node.statement, false) && node.statement.kind !== pxtc.SK.SwitchStatement)
|
|
138710
|
+
proc.emitLblDirect(l.brk);
|
|
138609
138711
|
}
|
|
138610
138712
|
function emitThrowStatement(node) {
|
|
138611
138713
|
emitBrk(node);
|
|
@@ -166649,8 +166751,8 @@ function saveThemeJson(cfg, localDir, packaged) {
|
|
|
166649
166751
|
const url = card.url || card.learnMoreUrl || card.buyUrl || (card.youTubeId && `https://youtu.be/${card.youTubeId}`);
|
|
166650
166752
|
tocmd += ` * [${card.name || card.title}](${url})
|
|
166651
166753
|
`;
|
|
166652
|
-
|
|
166653
|
-
|
|
166754
|
+
for (const term of (card.tags || []).concat(card.searchTerms || []))
|
|
166755
|
+
targetStrings[term] = term;
|
|
166654
166756
|
}));
|
|
166655
166757
|
});
|
|
166656
166758
|
nodeutil.writeFileSync(path.join(docsRoot, "docs/projects/SUMMARY.md"), tocmd, { encoding: "utf8" });
|
package/built/pxtcompiler.js
CHANGED
|
@@ -10178,6 +10178,7 @@ var ts;
|
|
|
10178
10178
|
let currUsingContext = null;
|
|
10179
10179
|
let needsUsingInfo = false;
|
|
10180
10180
|
let pendingFunctionDefinitions = [];
|
|
10181
|
+
const loopBlockFunctionDefinitions = {};
|
|
10181
10182
|
currNodeWave++;
|
|
10182
10183
|
if (opts.target.isNative) {
|
|
10183
10184
|
if (!opts.extinfo || !opts.extinfo.hexinfo) {
|
|
@@ -11478,10 +11479,14 @@ ${lbl}: .short 0xffff
|
|
|
11478
11479
|
args.push(irToNode(expr));
|
|
11479
11480
|
}
|
|
11480
11481
|
else {
|
|
11481
|
-
|
|
11482
|
-
|
|
11482
|
+
// Emitted functions apply their own defaults, including on
|
|
11483
|
+
// virtual/interface calls where the implementation may differ.
|
|
11484
|
+
// Shims and helpers may have no emitted parameter prologue.
|
|
11485
|
+
const needsCallerDefault = attrs.helper || attrs.shim;
|
|
11486
|
+
if (needsCallerDefault && !opts.unfetteredInitializers && !isNumericLiteral(prm.initializer)) {
|
|
11487
|
+
userError(9212, lf("only numbers, null, true and false supported as default arguments for shims and helpers"));
|
|
11483
11488
|
}
|
|
11484
|
-
args.push(prm.initializer);
|
|
11489
|
+
args.push(needsCallerDefault ? prm.initializer : irToNode(emitLit(undefined)));
|
|
11485
11490
|
}
|
|
11486
11491
|
}
|
|
11487
11492
|
else {
|
|
@@ -12220,12 +12225,8 @@ ${lbl}: .short 0xffff
|
|
|
12220
12225
|
}
|
|
12221
12226
|
}
|
|
12222
12227
|
}
|
|
12223
|
-
const destructuredParameters = [];
|
|
12224
12228
|
const fieldAssignmentParameters = [];
|
|
12225
12229
|
proc.args = getParameters(node).map((p, i) => {
|
|
12226
|
-
if (p.name.kind === pxtc.SK.ObjectBindingPattern) {
|
|
12227
|
-
destructuredParameters.push(p);
|
|
12228
|
-
}
|
|
12229
12230
|
if (node.kind == pxtc.SK.Constructor && isCtorField(p)) {
|
|
12230
12231
|
fieldAssignmentParameters.push(p);
|
|
12231
12232
|
}
|
|
@@ -12243,7 +12244,34 @@ ${lbl}: .short 0xffff
|
|
|
12243
12244
|
proc.emitExpr(l.storeDirect(tmp));
|
|
12244
12245
|
}
|
|
12245
12246
|
});
|
|
12246
|
-
|
|
12247
|
+
// Arity wrappers pad omitted arguments with undefined. Keep defaults in
|
|
12248
|
+
// the shared body so direct, virtual, interface and action calls all run
|
|
12249
|
+
// them, including native entries which can skip the arity wrapper.
|
|
12250
|
+
const attrs = parseComments(node);
|
|
12251
|
+
proc.args.forEach(l => {
|
|
12252
|
+
const parameter = l.def;
|
|
12253
|
+
// Simulator shim dummies must match the native caller-side behavior.
|
|
12254
|
+
if (parameter.initializer && !attrs.shim && !attrs.helper) {
|
|
12255
|
+
const supplied = proc.mkLabel("defaultarg");
|
|
12256
|
+
if (isThumb()) {
|
|
12257
|
+
// Tagged undefined is 0, so the guard is a bare
|
|
12258
|
+
// compare against zero rather than a runtime call.
|
|
12259
|
+
proc.emitJmp(supplied, l.load(), pxtc.ir.JmpMode.IfNotZero);
|
|
12260
|
+
}
|
|
12261
|
+
else {
|
|
12262
|
+
// The simulator holds real JS values; a raw zero test
|
|
12263
|
+
// would treat a supplied 0/false/null as omitted.
|
|
12264
|
+
proc.emitJmpZ(supplied, pxtc.ir.rtcall("pxt::eqq_bool", [l.load(), emitLit(undefined)]));
|
|
12265
|
+
}
|
|
12266
|
+
proc.emitExpr(l.storeByRef(emitEscapedExpression(parameter.initializer, "initializer")));
|
|
12267
|
+
proc.emitLbl(supplied);
|
|
12268
|
+
proc.stackEmpty();
|
|
12269
|
+
}
|
|
12270
|
+
if (parameter.name.kind === pxtc.SK.ObjectBindingPattern) {
|
|
12271
|
+
// Destructure the resolved argument, not the initializer again.
|
|
12272
|
+
emitVarOrParam(parameter, l.load(), typeOf(parameter));
|
|
12273
|
+
}
|
|
12274
|
+
});
|
|
12247
12275
|
// for constructor(public foo:number) generate this.foo = foo;
|
|
12248
12276
|
for (let p of fieldAssignmentParameters) {
|
|
12249
12277
|
let idx = fieldIndexCore(proc.classInfo, getFieldInfo(proc.classInfo, getName(p)), false);
|
|
@@ -12286,11 +12314,25 @@ ${lbl}: .short 0xffff
|
|
|
12286
12314
|
if (info.capturedVars.length &&
|
|
12287
12315
|
info.usedBeforeDecl &&
|
|
12288
12316
|
node.kind == pxtc.SK.FunctionDeclaration && !bin.finalPass) {
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12317
|
+
// Synthetic captures (such as thisParameter) are available before
|
|
12318
|
+
// source declarations, but may not have a source position.
|
|
12319
|
+
const capturePos = (capture) => capture.pos == null ? -1 : capture.pos;
|
|
12320
|
+
info.capturedVars.sort((a, b) => capturePos(b) - capturePos(a));
|
|
12321
|
+
const scope = node.parent;
|
|
12322
|
+
if (scope.kind === pxtc.SK.Block && inLoop(scope) && info.capturedVars.every(capture => capturePos(capture) < scope.pos)) {
|
|
12323
|
+
// All captures precede this loop block. Defining the function at
|
|
12324
|
+
// the last captured variable would hoist it out of the iteration.
|
|
12325
|
+
const id = getNodeId(scope);
|
|
12326
|
+
const definitions = loopBlockFunctionDefinitions[id] || (loopBlockFunctionDefinitions[id] = []);
|
|
12327
|
+
if (definitions.indexOf(node) < 0)
|
|
12328
|
+
definitions.push(node);
|
|
12329
|
+
}
|
|
12330
|
+
else {
|
|
12331
|
+
const vinfo = getVarInfo(info.capturedVars[0]);
|
|
12332
|
+
if (!vinfo.functionsToDefine)
|
|
12333
|
+
vinfo.functionsToDefine = [];
|
|
12334
|
+
vinfo.functionsToDefine.push(node);
|
|
12335
|
+
}
|
|
12294
12336
|
}
|
|
12295
12337
|
// nothing should be on work list in final pass - everything should be already marked as used
|
|
12296
12338
|
pxtc.assert(!bin.finalPass || usedWorkList.length == 0, "!bin.finalPass || usedWorkList.length == 0");
|
|
@@ -13247,6 +13289,11 @@ ${lbl}: .short 0xffff
|
|
|
13247
13289
|
function emitSpreadElementExpression(node) { }
|
|
13248
13290
|
function emitYieldExpression(node) { }
|
|
13249
13291
|
function emitBlock(node) {
|
|
13292
|
+
const definitions = bin.finalPass && loopBlockFunctionDefinitions[getNodeId(node)];
|
|
13293
|
+
if (definitions) {
|
|
13294
|
+
pxtc.U.pushRange(pendingFunctionDefinitions, definitions);
|
|
13295
|
+
flushHoistedFunctionDefinitions();
|
|
13296
|
+
}
|
|
13250
13297
|
node.statements.forEach(emit);
|
|
13251
13298
|
}
|
|
13252
13299
|
function checkForLetOrConst(declList) {
|
|
@@ -13432,16 +13479,45 @@ ${lbl}: .short 0xffff
|
|
|
13432
13479
|
proc.emitExpr(v);
|
|
13433
13480
|
proc.stackEmpty();
|
|
13434
13481
|
}
|
|
13482
|
+
function emitForIterationBindings(declList) {
|
|
13483
|
+
// Only `let` loop headers create copied bindings. `const` and variables
|
|
13484
|
+
// declared outside the loop retain their existing binding semantics.
|
|
13485
|
+
if (!declList || !(declList.flags & ts.NodeFlags.Let))
|
|
13486
|
+
return;
|
|
13487
|
+
const copyBinding = (decl) => {
|
|
13488
|
+
if (decl.name.kind === pxtc.SK.ObjectBindingPattern || decl.name.kind === pxtc.SK.ArrayBindingPattern) {
|
|
13489
|
+
decl.name.elements.forEach(element => {
|
|
13490
|
+
if (element.kind === pxtc.SK.BindingElement)
|
|
13491
|
+
copyBinding(element);
|
|
13492
|
+
});
|
|
13493
|
+
return;
|
|
13494
|
+
}
|
|
13495
|
+
const loc = proc.localIndex(decl);
|
|
13496
|
+
if (!loc || !loc.isByRefLocal())
|
|
13497
|
+
return;
|
|
13498
|
+
// Populate the new box before replacing the old cell. Closures already
|
|
13499
|
+
// created keep the old box; sibling closures in this iteration share the new one.
|
|
13500
|
+
const next = pxtc.ir.shared(pxtc.ir.rtcall("pxtrt::mklocRef", []));
|
|
13501
|
+
proc.emitExpr(pxtc.ir.rtcall("pxtrt::stlocRef", [next, loc.load()]));
|
|
13502
|
+
proc.emitExpr(loc.storeDirect(next));
|
|
13503
|
+
proc.stackEmpty();
|
|
13504
|
+
};
|
|
13505
|
+
declList.declarations.forEach(copyBinding);
|
|
13506
|
+
}
|
|
13435
13507
|
function emitForStatement(node) {
|
|
13508
|
+
let declList;
|
|
13436
13509
|
if (node.initializer && node.initializer.kind == pxtc.SK.VariableDeclarationList) {
|
|
13437
|
-
|
|
13438
|
-
|
|
13510
|
+
declList = node.initializer;
|
|
13511
|
+
checkForLetOrConst(declList);
|
|
13512
|
+
declList.declarations.forEach(emit);
|
|
13439
13513
|
}
|
|
13440
13514
|
else {
|
|
13441
13515
|
emitExprAsStmt(node.initializer);
|
|
13442
13516
|
}
|
|
13443
13517
|
emitBrk(node);
|
|
13444
13518
|
let l = getLabels(node);
|
|
13519
|
+
// Initializer closures keep their own binding, separate from the first condition/body.
|
|
13520
|
+
emitForIterationBindings(declList);
|
|
13445
13521
|
proc.emitLblDirect(l.fortop);
|
|
13446
13522
|
if (node.condition) {
|
|
13447
13523
|
emitBrk(node.condition);
|
|
@@ -13449,6 +13525,8 @@ ${lbl}: .short 0xffff
|
|
|
13449
13525
|
}
|
|
13450
13526
|
emit(node.statement);
|
|
13451
13527
|
proc.emitLblDirect(l.cont);
|
|
13528
|
+
// A continue must also create the next binding BEFORE evaluating the incrementor.
|
|
13529
|
+
emitForIterationBindings(declList);
|
|
13452
13530
|
emitExprAsStmt(node.incrementor);
|
|
13453
13531
|
proc.emitJmp(l.fortop);
|
|
13454
13532
|
proc.emitLblDirect(l.brk);
|
|
@@ -13508,6 +13586,11 @@ ${lbl}: .short 0xffff
|
|
|
13508
13586
|
};
|
|
13509
13587
|
// c = a[i]
|
|
13510
13588
|
if (iterVar) {
|
|
13589
|
+
// A mutable captured for-of variable needs a fresh box for each element,
|
|
13590
|
+
// rather than overwriting the box retained by closures from earlier elements.
|
|
13591
|
+
if (iterVar.isByRefLocal()) {
|
|
13592
|
+
proc.emitExpr(iterVar.storeDirect(pxtc.ir.rtcall("pxtrt::mklocRef", [])));
|
|
13593
|
+
}
|
|
13511
13594
|
proc.emitExpr(iterVar.storeByRef(pxtc.ir.rtcall(indexer, [collectionVar.loadCore(), toInt(intVarIter.loadCore())])));
|
|
13512
13595
|
emitBrk(node.initializer);
|
|
13513
13596
|
}
|
|
@@ -13621,7 +13704,10 @@ ${lbl}: .short 0xffff
|
|
|
13621
13704
|
function emitLabeledStatement(node) {
|
|
13622
13705
|
let l = getLabels(node.statement);
|
|
13623
13706
|
emit(node.statement);
|
|
13624
|
-
|
|
13707
|
+
// Loops and switches define their own break target. Emitting it twice
|
|
13708
|
+
// is tolerated by simjs but rejected by the native assembler.
|
|
13709
|
+
if (!ts.isIterationStatement(node.statement, false) && node.statement.kind !== pxtc.SK.SwitchStatement)
|
|
13710
|
+
proc.emitLblDirect(l.brk);
|
|
13625
13711
|
}
|
|
13626
13712
|
function emitThrowStatement(node) {
|
|
13627
13713
|
emitBrk(node);
|
package/built/pxtlib.js
CHANGED
|
@@ -10373,7 +10373,7 @@ var pxt;
|
|
|
10373
10373
|
if (cards && !Array.isArray(cards))
|
|
10374
10374
|
cards = [cards];
|
|
10375
10375
|
if (cards === null || cards === void 0 ? void 0 : cards.length)
|
|
10376
|
-
return cards;
|
|
10376
|
+
return cards.map(normalizeCodeCardStringArrays);
|
|
10377
10377
|
// not json, try parsing as sequence of key,value pairs, with line splits
|
|
10378
10378
|
cards = md.split(/^---$/gm)
|
|
10379
10379
|
.filter(cmd => !!cmd)
|
|
@@ -10399,7 +10399,7 @@ var pxt;
|
|
|
10399
10399
|
})
|
|
10400
10400
|
.filter(cc => !!cc);
|
|
10401
10401
|
if (cards === null || cards === void 0 ? void 0 : cards.length)
|
|
10402
|
-
return cards;
|
|
10402
|
+
return cards.map(normalizeCodeCardStringArrays);
|
|
10403
10403
|
return undefined;
|
|
10404
10404
|
}
|
|
10405
10405
|
gallery_1.parseCodeCards = parseCodeCards;
|
|
@@ -10440,9 +10440,18 @@ var pxt;
|
|
|
10440
10440
|
// legacy JSON format
|
|
10441
10441
|
cards = pxt.Util.jsonTryParse(el.textContent);
|
|
10442
10442
|
}
|
|
10443
|
-
return !!(cards === null || cards === void 0 ? void 0 : cards.length) && cards;
|
|
10443
|
+
return !!(cards === null || cards === void 0 ? void 0 : cards.length) && cards.map(normalizeCodeCardStringArrays);
|
|
10444
10444
|
}
|
|
10445
10445
|
gallery_1.parseCodeCardsHtml = parseCodeCardsHtml;
|
|
10446
|
+
function normalizeCodeCardStringArrays(card) {
|
|
10447
|
+
["tags", "searchTerms"].forEach(field => {
|
|
10448
|
+
const value = card[field];
|
|
10449
|
+
if (typeof value === "string") {
|
|
10450
|
+
card[field] = value.split(',').map(term => term.trim()).filter(term => !!term);
|
|
10451
|
+
}
|
|
10452
|
+
});
|
|
10453
|
+
return card;
|
|
10454
|
+
}
|
|
10446
10455
|
function parseGalleryMardown(md) {
|
|
10447
10456
|
if (!md)
|
|
10448
10457
|
return [];
|
|
@@ -18776,18 +18785,25 @@ var ts;
|
|
|
18776
18785
|
pxt.debug(`Localized block has extra or missing parameters`);
|
|
18777
18786
|
return false;
|
|
18778
18787
|
}
|
|
18788
|
+
if (!!a.thisParameter != !!b.thisParameter
|
|
18789
|
+
|| (a.thisParameter && !isEquivalentParameter(a.thisParameter, b.thisParameter))) {
|
|
18790
|
+
pxt.debug(`Localized block's this parameter shadow block or definition name does not match`);
|
|
18791
|
+
return false;
|
|
18792
|
+
}
|
|
18779
18793
|
for (const aParam of a.parameters) {
|
|
18780
18794
|
const bParam = b.actualNameToParam[aParam.actualName];
|
|
18781
|
-
if (!bParam
|
|
18782
|
-
|| aParam.type != bParam.type
|
|
18783
|
-
|| aParam.shadowBlockId != bParam.shadowBlockId
|
|
18784
|
-
|| aParam.definitionName != bParam.definitionName) {
|
|
18795
|
+
if (!bParam || !isEquivalentParameter(aParam, bParam)) {
|
|
18785
18796
|
pxt.debug(`Parameter ${aParam.actualName} type, shadow block, or definition name does not match after localization`);
|
|
18786
18797
|
return false;
|
|
18787
18798
|
}
|
|
18788
18799
|
}
|
|
18789
18800
|
return true;
|
|
18790
18801
|
}
|
|
18802
|
+
function isEquivalentParameter(a, b) {
|
|
18803
|
+
return a.type == b.type
|
|
18804
|
+
&& a.shadowBlockId == b.shadowBlockId
|
|
18805
|
+
&& a.definitionName == b.definitionName;
|
|
18806
|
+
}
|
|
18791
18807
|
function emptyExtInfo() {
|
|
18792
18808
|
let cs = pxt.appTarget.compileService;
|
|
18793
18809
|
if (!cs)
|