|
2 | 2 |
|
3 | 3 | import com.google.common.collect.*; |
4 | 4 | import de.peeeq.wurstscript.WLogger; |
| 5 | +import de.peeeq.wurstscript.ast.ClassDef; |
5 | 6 | import de.peeeq.wurstscript.ast.PackageOrGlobal; |
6 | 7 | import de.peeeq.wurstscript.ast.WPackage; |
7 | 8 | import de.peeeq.wurstscript.attributes.CompileError; |
8 | 9 | import de.peeeq.wurstscript.jassIm.*; |
9 | 10 | import de.peeeq.wurstscript.translation.imtojass.ImAttrType; |
10 | 11 | import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher; |
| 12 | +import de.peeeq.wurstscript.translation.lua.translation.RemoveGarbage; |
11 | 13 | import org.eclipse.jdt.annotation.Nullable; |
12 | 14 | import org.jetbrains.annotations.NotNull; |
13 | 15 |
|
@@ -81,6 +83,26 @@ public void transform() { |
81 | 83 | dbg(summary("after removeGenericConstructs")); |
82 | 84 |
|
83 | 85 | dbg(checkDanglingMethodRefs("end")); |
| 86 | + |
| 87 | + // TODO fix or remove this check |
| 88 | +// assertNoUnspecializedGenericGlobals(); |
| 89 | + } |
| 90 | + |
| 91 | + private void assertNoUnspecializedGenericGlobals() { |
| 92 | + prog.accept(new Element.DefaultVisitor() { |
| 93 | + @Override public void visit(ImVarAccess va) { |
| 94 | + super.visit(va); |
| 95 | + if (globalToClass.containsKey(va.getVar())) { |
| 96 | + throw new CompileError(va, "Unspecialized generic global still used: " + va.getVar().getName()); |
| 97 | + } |
| 98 | + } |
| 99 | + @Override public void visit(ImVarArrayAccess vaa) { |
| 100 | + super.visit(vaa); |
| 101 | + if (globalToClass.containsKey(vaa.getVar())) { |
| 102 | + throw new CompileError(vaa, "Unspecialized generic global array still used: " + vaa.getVar().getName()); |
| 103 | + } |
| 104 | + } |
| 105 | + }); |
84 | 106 | } |
85 | 107 |
|
86 | 108 | private void makeNullAssignmentsSafe() { |
@@ -259,7 +281,12 @@ private String summary(String phase) { |
259 | 281 | private void removeNonSpecializedGlobals() { |
260 | 282 | for (ImVar imVar : specializedGlobals.rowKeySet()) { |
261 | 283 | prog.getGlobals().remove(imVar); |
262 | | - prog.getGlobalInits().remove(imVar); |
| 284 | + List<ImSet> inits = prog.getGlobalInits().remove(imVar); |
| 285 | + if (inits != null) { |
| 286 | + for (ImSet init : inits) { |
| 287 | + init.replaceBy(ImHelper.nullExpr()); |
| 288 | + } |
| 289 | + } |
263 | 290 | } |
264 | 291 | } |
265 | 292 |
|
@@ -399,29 +426,79 @@ private void moveFunctionsOutOfClass(ImClass c) { |
399 | 426 | * These are the "static" fields that need specialization |
400 | 427 | */ |
401 | 428 | private void identifyGenericGlobals() { |
402 | | - // Build a map of class name to class for quick lookup |
403 | | - Map<String, ImClass> classMap = new HashMap<>(); |
| 429 | + // Only include "relevant" classes: new-generic or subclass of new-generic. |
| 430 | + Map<String, ImClass> relevantClassMap = buildRelevantClassMap(); |
| 431 | + |
| 432 | + for (ImVar global : prog.getGlobals()) { |
| 433 | + ImClass owner = resolveOwningClassFromTrace(global, relevantClassMap); |
| 434 | + if (owner == null) { |
| 435 | + continue; // not defined inside a class (package/global constant, etc.) |
| 436 | + } |
| 437 | + |
| 438 | + // This global belongs to a relevant (new-generic or inheriting) class: |
| 439 | + globalToClass.put(global, owner); |
| 440 | + WLogger.trace("Identified generic static-field global: " + global.getName() |
| 441 | + + " of type " + global.getType() |
| 442 | + + " belonging to class " + owner.getName()); |
| 443 | + } |
| 444 | + } |
| 445 | + |
| 446 | + /** |
| 447 | + * Build a map of class-name -> ImClass, but only for "relevant" classes: |
| 448 | + * - the class is new-generic (has typeVariables) |
| 449 | + * - OR any of its superclasses is new-generic (transitively) |
| 450 | + */ |
| 451 | + private Map<String, ImClass> buildRelevantClassMap() { |
| 452 | + Map<String, ImClass> m = new HashMap<>(); |
| 453 | + IdentityHashMap<ImClass, Boolean> memo = new IdentityHashMap<>(); |
| 454 | + |
404 | 455 | for (ImClass c : prog.getClasses()) { |
405 | | - classMap.put(c.getName(), c); |
| 456 | + if (isNewGenericOrExtendsNewGeneric(c, memo)) { |
| 457 | + m.put(c.getName(), c); |
| 458 | + } |
406 | 459 | } |
| 460 | + return m; |
| 461 | + } |
407 | 462 |
|
408 | | - // Check each global variable to see if it belongs to a generic class |
409 | | - for (ImVar global : prog.getGlobals()) { |
410 | | - // Global variable names for static fields follow the pattern: ClassName_fieldName |
411 | | - String varName = global.getName(); |
412 | | - int underscoreIdx = varName.indexOf('_'); |
413 | | - if (underscoreIdx > 0) { |
414 | | - String potentialClassName = varName.substring(0, underscoreIdx); |
415 | | - ImClass owningClass = classMap.get(potentialClassName); |
416 | | - |
417 | | - if (owningClass != null && !owningClass.getTypeVariables().isEmpty()) { |
418 | | - // This global belongs to a generic class |
419 | | - globalToClass.put(global, owningClass); |
420 | | - WLogger.trace("Identified generic global: " + varName + " of type " + global.getType() + |
421 | | - " belonging to class " + owningClass.getName()); |
| 463 | + private boolean isNewGenericOrExtendsNewGeneric(ImClass c, IdentityHashMap<ImClass, Boolean> memo) { |
| 464 | + Boolean cached = memo.get(c); |
| 465 | + if (cached != null) return cached; |
| 466 | + |
| 467 | + boolean res = !c.getTypeVariables().isEmpty(); |
| 468 | + if (!res) { |
| 469 | + for (ImClassType sc : c.getSuperClasses()) { |
| 470 | + ImClass sup = sc.getClassDef(); |
| 471 | + if (sup != null && isNewGenericOrExtendsNewGeneric(sup, memo)) { |
| 472 | + res = true; |
| 473 | + break; |
422 | 474 | } |
423 | 475 | } |
424 | 476 | } |
| 477 | + |
| 478 | + memo.put(c, res); |
| 479 | + return res; |
| 480 | + } |
| 481 | + |
| 482 | + /** |
| 483 | + * Resolve owning class for a global via trace: |
| 484 | + * - if the global's trace source is inside a class, return the matching ImClass (if relevant) |
| 485 | + * - otherwise return null |
| 486 | + */ |
| 487 | + private @Nullable ImClass resolveOwningClassFromTrace(ImVar global, Map<String, ImClass> relevantClassMap) { |
| 488 | + if (global.getTrace() == null) return null; |
| 489 | + |
| 490 | + // This is the only assumption you may need to adapt if your ImTrace API differs: |
| 491 | + de.peeeq.wurstscript.ast.Element srcObj = global.getTrace(); // expected to be a wurst AST Element |
| 492 | + if (srcObj == null) return null; |
| 493 | + |
| 494 | + @Nullable ClassDef classDef = srcObj.attrNearestClassDef(); |
| 495 | + if (classDef == null) return null; |
| 496 | + |
| 497 | + // Get the class name from the AST (no global-name parsing). |
| 498 | + String className = classDef.getNameId().getName(); |
| 499 | + |
| 500 | + // Only accept if it is one of the relevant classes (new-generic or inherits new-generic). |
| 501 | + return relevantClassMap.get(className); |
425 | 502 | } |
426 | 503 |
|
427 | 504 | /** |
|
0 commit comments