mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-08-02 15:32:15 +00:00
Fixed #1937 AS3 - declarations vs null
This commit is contained in:
@@ -26,6 +26,7 @@ All notable changes to this project will be documented in this file.
|
||||
- [#1936] AS3 Direct editation - scope of nested functions
|
||||
- AS3 - empty P-code shown on clicking script
|
||||
- [#1888] AS3 - Coerces, module operator
|
||||
- [#1937] AS3 - declarations vs null
|
||||
|
||||
## [18.3.2] - 2023-01-10
|
||||
### Removed
|
||||
@@ -2889,6 +2890,7 @@ All notable changes to this project will be documented in this file.
|
||||
[#1801]: https://www.free-decompiler.com/flash/issues/1801
|
||||
[#1892]: https://www.free-decompiler.com/flash/issues/1892
|
||||
[#1936]: https://www.free-decompiler.com/flash/issues/1936
|
||||
[#1937]: https://www.free-decompiler.com/flash/issues/1937
|
||||
[#1935]: https://www.free-decompiler.com/flash/issues/1935
|
||||
[#1931]: https://www.free-decompiler.com/flash/issues/1931
|
||||
[#1934]: https://www.free-decompiler.com/flash/issues/1934
|
||||
|
||||
@@ -1193,14 +1193,14 @@ public class AVM2Code implements Cloneable {
|
||||
}
|
||||
|
||||
public GraphTextWriter toASMSource(ABC abc, AVM2ConstantPool constants, MethodInfo info, MethodBody body, List<Integer> outputMap, ScriptExportMode exportMode, GraphTextWriter writer) {
|
||||
|
||||
|
||||
if (info != null) {
|
||||
writer.appendNoHilight("method");
|
||||
if (Configuration.indentAs3PCode.get()) {
|
||||
writer.indent();
|
||||
}
|
||||
writer.newLine();
|
||||
|
||||
|
||||
info.toASMSource(constants, writer);
|
||||
}
|
||||
writer.newLine();
|
||||
@@ -1780,22 +1780,31 @@ public class AVM2Code implements Cloneable {
|
||||
} else if (assignment.value instanceof CoerceAVM2Item) {
|
||||
vtype = ((CoerceAVM2Item) assignment.value).typeObj;
|
||||
} else if (assignment instanceof LocalRegAVM2Item) { //for..in
|
||||
vtype = ((LocalRegAVM2Item)assignment).type;
|
||||
vtype = ((LocalRegAVM2Item) assignment).type;
|
||||
} else if (assignment instanceof GetSlotAVM2Item) { //for..in
|
||||
vtype = ((GetSlotAVM2Item)assignment).slotType;
|
||||
vtype = ((GetSlotAVM2Item) assignment).slotType;
|
||||
} else if ((assignment.value instanceof SimpleValue) && ((SimpleValue) assignment.value).isSimpleValue()) {
|
||||
vtype = assignment.value.returnType();
|
||||
}
|
||||
}
|
||||
|
||||
boolean isNull = false;
|
||||
if (vtype.equals(new TypeItem(DottedChain.NULL))) {
|
||||
vtype = TypeItem.UNBOUNDED;
|
||||
}
|
||||
|
||||
if (declaredRegisters[reg] == null) {
|
||||
if (declaredRegisters[reg] == null) {
|
||||
declaredRegisters[reg] = new DeclarationAVM2Item(assignment, vtype);
|
||||
if (assignment instanceof SetTypeAVM2Item) {
|
||||
((SetTypeAVM2Item) assignment).setDeclaration(declaredRegisters[reg]);
|
||||
}
|
||||
declaredRegisters[reg].typeIsNull = isNull;
|
||||
return declaredRegisters[reg];
|
||||
}
|
||||
|
||||
if (declaredRegisters[reg].type == TypeItem.UNBOUNDED) {
|
||||
if (declaredRegisters[reg].typeIsNull) {
|
||||
declaredRegisters[reg].type = vtype;
|
||||
declaredRegisters[reg].typeIsNull = isNull;
|
||||
} else if (declaredRegisters[reg].type == TypeItem.UNBOUNDED) {
|
||||
|
||||
} else if (!declaredRegisters[reg].type.equals(vtype)) { //already declared with different type
|
||||
declaredRegisters[reg].type = TypeItem.UNBOUNDED;
|
||||
@@ -1896,7 +1905,7 @@ public class AVM2Code implements Cloneable {
|
||||
Slot sl = new Slot(new NewActivationAVM2Item(null, null), propertyMultiName);
|
||||
if (!paramNames.contains(propertyName)) {
|
||||
if (traits.containsKey(propertyName) && !beginDeclaredSlotsNames.contains(propertyName)) {
|
||||
hasPrevReference.setVal(true);
|
||||
hasPrevReference.setVal(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1956,7 +1965,7 @@ public class AVM2Code implements Cloneable {
|
||||
int reg = ((LocalRegAVM2Item) fi.expression.object).regIndex;
|
||||
fi.expression.object = handleDeclareReg(minreg, fi.expression.object, declaredRegisters, declaredSlots, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (GraphTargetItem subItem : itemsOnLine) {
|
||||
if (subItem instanceof SetLocalAVM2Item) {
|
||||
@@ -1987,11 +1996,11 @@ public class AVM2Code implements Cloneable {
|
||||
sp.setDeclaration(d);
|
||||
declaredPropsDec.add(d);
|
||||
declaredProperties.add(propName.resolvedMultinameName);
|
||||
|
||||
|
||||
Slot sl = new Slot(new NewActivationAVM2Item(null, null), abc.constants.getMultiname(tsc.name_index));
|
||||
declaredSlotsDec.add(d);
|
||||
declaredSlots.add(sl);
|
||||
|
||||
|
||||
if (subItem == currentItem) {
|
||||
items.set(i, d);
|
||||
} else {
|
||||
@@ -2025,7 +2034,7 @@ public class AVM2Code implements Cloneable {
|
||||
ssti.setDeclaration(d);
|
||||
declaredSlotsDec.add(d);
|
||||
declaredSlots.add(sl);
|
||||
|
||||
|
||||
declaredPropsDec.add(d);
|
||||
declaredProperties.add(slotPropertyName);
|
||||
|
||||
@@ -2088,10 +2097,10 @@ public class AVM2Code implements Cloneable {
|
||||
localRegTypes.put(i + 1, AbcIndexing.multinameToType(abc.method_info.get(methodIndex).param_types[i], abc.constants));
|
||||
}
|
||||
|
||||
ScopeStack prevScopeStack = (ScopeStack)scopeStack.clone();
|
||||
try{
|
||||
ScopeStack prevScopeStack = (ScopeStack) scopeStack.clone();
|
||||
try {
|
||||
list = AVM2Graph.translateViaGraph(null, callStack, abcIndex, path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, scopeStack, localRegNames, localRegTypes, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs, thisHasDefaultToPrimitive);
|
||||
} catch(SecondPassException spe) {
|
||||
} catch (SecondPassException spe) {
|
||||
list = AVM2Graph.translateViaGraph(spe.getData(), callStack, abcIndex, path, this, abc, body, isStatic, scriptIndex, classIndex, localRegs, prevScopeStack, localRegNames, localRegTypes, fullyQualifiedNames, staticOperation, localRegAssigmentIps, refs, thisHasDefaultToPrimitive);
|
||||
}
|
||||
if (initTraits != null) {
|
||||
@@ -2549,19 +2558,19 @@ public class AVM2Code implements Cloneable {
|
||||
|
||||
public int removeTraps(Trait trait, int methodInfo, MethodBody body, ABC abc, int scriptIndex, int classIndex, boolean isStatic, String path) throws InterruptedException {
|
||||
SWFDecompilerPlugin.fireAvm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
try ( Statistics s = new Statistics("AVM2DeobfuscatorGetSet")) {
|
||||
try (Statistics s = new Statistics("AVM2DeobfuscatorGetSet")) {
|
||||
new AVM2DeobfuscatorGetSet().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
}
|
||||
try ( Statistics s = new Statistics("AVM2DeobfuscatorSimple")) {
|
||||
try (Statistics s = new Statistics("AVM2DeobfuscatorSimple")) {
|
||||
new AVM2DeobfuscatorSimpleOld().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
}
|
||||
try ( Statistics s = new Statistics("AVM2DeobfuscatorRegisters")) {
|
||||
try (Statistics s = new Statistics("AVM2DeobfuscatorRegisters")) {
|
||||
new AVM2DeobfuscatorRegistersOld().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
}
|
||||
try ( Statistics s = new Statistics("AVM2DeobfuscatorJumps")) {
|
||||
try (Statistics s = new Statistics("AVM2DeobfuscatorJumps")) {
|
||||
new AVM2DeobfuscatorJumps().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
}
|
||||
try ( Statistics s = new Statistics("AVM2DeobfuscatorZeroJumpsNullPushes")) {
|
||||
try (Statistics s = new Statistics("AVM2DeobfuscatorZeroJumpsNullPushes")) {
|
||||
new AVM2DeobfuscatorZeroJumpsNullPushes().avm2CodeRemoveTraps(path, classIndex, isStatic, scriptIndex, abc, trait, methodInfo, body);
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.jpexs.decompiler.graph.CompilationException;
|
||||
import com.jpexs.decompiler.graph.DottedChain;
|
||||
import com.jpexs.decompiler.graph.GraphSourceItem;
|
||||
import com.jpexs.decompiler.graph.GraphTargetItem;
|
||||
import com.jpexs.decompiler.graph.SimpleValue;
|
||||
import com.jpexs.decompiler.graph.SourceGenerator;
|
||||
import com.jpexs.decompiler.graph.TypeItem;
|
||||
import com.jpexs.decompiler.graph.model.LocalData;
|
||||
@@ -35,7 +36,7 @@ import java.util.Set;
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class NullAVM2Item extends AVM2Item {
|
||||
public class NullAVM2Item extends AVM2Item implements SimpleValue {
|
||||
|
||||
public NullAVM2Item(GraphSourceItem instruction, GraphSourceItem lineStartIns) {
|
||||
super(instruction, lineStartIns, PRECEDENCE_PRIMARY);
|
||||
@@ -92,4 +93,9 @@ public class NullAVM2Item extends AVM2Item {
|
||||
int hash = 3;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSimpleValue() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ public class DeclarationAVM2Item extends AVM2Item {
|
||||
public GraphTargetItem assignment;
|
||||
|
||||
public GraphTargetItem type;
|
||||
|
||||
public boolean typeIsNull = false;
|
||||
|
||||
public boolean showValue = true;
|
||||
|
||||
|
||||
@@ -348,6 +348,22 @@ public class ActionScript3ClassicAirDecompileTest extends ActionScript3Decompile
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeclarationInterface() {
|
||||
decompileMethod("classic_air", "testDeclarationInterface", "var i:MyIFace = null;\r\n"
|
||||
+ "var n:int = 2;\r\n"
|
||||
+ "switch(n)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "case 0:\r\n"
|
||||
+ "i = new MyClass();\r\n"
|
||||
+ "break;\r\n"
|
||||
+ "case 1:\r\n"
|
||||
+ "i = new MyClass2();\r\n"
|
||||
+ "}\r\n"
|
||||
+ "return i;\r\n",
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeclarations() {
|
||||
decompileMethod("classic_air", "testDeclarations", "var vall:* = undefined;\r\n"
|
||||
|
||||
@@ -347,6 +347,22 @@ public class ActionScript3ClassicDecompileTest extends ActionScript3DecompileTes
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeclarationInterface() {
|
||||
decompileMethod("classic", "testDeclarationInterface", "var i:MyIFace = null;\r\n"
|
||||
+ "var n:int = 2;\r\n"
|
||||
+ "switch(n)\r\n"
|
||||
+ "{\r\n"
|
||||
+ "case 0:\r\n"
|
||||
+ "i = new MyClass();\r\n"
|
||||
+ "break;\r\n"
|
||||
+ "case 1:\r\n"
|
||||
+ "i = new MyClass2();\r\n"
|
||||
+ "}\r\n"
|
||||
+ "return i;\r\n",
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeclarations() {
|
||||
decompileMethod("classic", "testDeclarations", "var vall:* = undefined;\r\n"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
277
libsrc/ffdec_lib/testdata/as3_new/src/Main.as
vendored
277
libsrc/ffdec_lib/testdata/as3_new/src/Main.as
vendored
@@ -1,152 +1,153 @@
|
||||
package
|
||||
{
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import tests.*;
|
||||
import tests_classes.mypackage1.SetupMyPackage1;
|
||||
import tests_classes.mypackage2.SetupMyPackage2;
|
||||
import tests_classes.mypackage3.SetupMyPackage3;
|
||||
import tests_classes.*;
|
||||
import tests_edit.*;
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Main extends Sprite
|
||||
{
|
||||
TestActivationArguments;
|
||||
TestArguments;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import tests.*;
|
||||
import tests_classes.mypackage1.SetupMyPackage1;
|
||||
import tests_classes.mypackage2.SetupMyPackage2;
|
||||
import tests_classes.mypackage3.SetupMyPackage3;
|
||||
import tests_classes.*;
|
||||
import tests_edit.*;
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class Main extends Sprite
|
||||
{
|
||||
TestActivationArguments;
|
||||
TestArguments;
|
||||
TestCallCall;
|
||||
TestCallLocal;
|
||||
TestCatchFinally;
|
||||
TestChain2;
|
||||
TestChainedAssignments;
|
||||
TestCallLocal;
|
||||
TestCatchFinally;
|
||||
TestChain2;
|
||||
TestChainedAssignments;
|
||||
TestCollidingTry;
|
||||
TestComplexExpressions;
|
||||
TestContinueLevels;
|
||||
TestConvert;
|
||||
TestComma;
|
||||
TestCompoundAssignments;
|
||||
TestDecl2;
|
||||
TestDeclarations;
|
||||
TestDeobfuscation;
|
||||
TestDefaultNotLastGrouped;
|
||||
TestDotParent;
|
||||
TestDoWhile;
|
||||
TestDoWhile2;
|
||||
TestDoWhile3;
|
||||
TestDoWhile4;
|
||||
TestExpressions;
|
||||
TestFinallyZeroJump;
|
||||
TestFor;
|
||||
TestForAnd;
|
||||
TestForBreak;
|
||||
TestForContinue;
|
||||
TestForEach;
|
||||
TestForEachObjectArray;
|
||||
TestForEachObjectAttribute;
|
||||
TestForEachReturn;
|
||||
TestForEachReturn2;
|
||||
TestForGoto;
|
||||
TestForIn;
|
||||
TestForInIf;
|
||||
TestForInReturn;
|
||||
TestForInSwitch;
|
||||
TestForXml;
|
||||
TestGotos;
|
||||
TestGotos2;
|
||||
TestGotos3;
|
||||
TestGotos4;
|
||||
TestGotos5;
|
||||
TestGotos6;
|
||||
TestGotos7;
|
||||
TestHello;
|
||||
TestIf;
|
||||
TestIfElse;
|
||||
TestIfFinally;
|
||||
TestIfInIf;
|
||||
TestIfTry;
|
||||
TestIgnoreAndOr;
|
||||
TestContinueLevels;
|
||||
TestConvert;
|
||||
TestComma;
|
||||
TestCompoundAssignments;
|
||||
TestDecl2;
|
||||
TestDeclarations;
|
||||
TestDeclarationInterface;
|
||||
TestDeobfuscation;
|
||||
TestDefaultNotLastGrouped;
|
||||
TestDotParent;
|
||||
TestDoWhile;
|
||||
TestDoWhile2;
|
||||
TestDoWhile3;
|
||||
TestDoWhile4;
|
||||
TestExpressions;
|
||||
TestFinallyZeroJump;
|
||||
TestFor;
|
||||
TestForAnd;
|
||||
TestForBreak;
|
||||
TestForContinue;
|
||||
TestForEach;
|
||||
TestForEachObjectArray;
|
||||
TestForEachObjectAttribute;
|
||||
TestForEachReturn;
|
||||
TestForEachReturn2;
|
||||
TestForGoto;
|
||||
TestForIn;
|
||||
TestForInIf;
|
||||
TestForInReturn;
|
||||
TestForInSwitch;
|
||||
TestForXml;
|
||||
TestGotos;
|
||||
TestGotos2;
|
||||
TestGotos3;
|
||||
TestGotos4;
|
||||
TestGotos5;
|
||||
TestGotos6;
|
||||
TestGotos7;
|
||||
TestHello;
|
||||
TestIf;
|
||||
TestIfElse;
|
||||
TestIfFinally;
|
||||
TestIfInIf;
|
||||
TestIfTry;
|
||||
TestIgnoreAndOr;
|
||||
TestImplicitCoerce;
|
||||
TestImportedVar;
|
||||
TestInc2;
|
||||
TestIncDec;
|
||||
TestInlineFunctions;
|
||||
TestInlineFunctions2;
|
||||
TestInnerFunctions;
|
||||
TestInnerFunctionScope;
|
||||
TestInnerIf;
|
||||
TestInnerTry;
|
||||
TestLogicalComputing;
|
||||
TestManualConvert;
|
||||
TestMissingDefault;
|
||||
TestMultipleCondition;
|
||||
TestNamedAnonFunctions;
|
||||
TestNames;
|
||||
TestNegate;
|
||||
TestNumberCall;
|
||||
TestOptionalParameters;
|
||||
TestParamNames;
|
||||
TestParamsCount;
|
||||
TestPrecedence;
|
||||
TestPrecedenceX;
|
||||
TestProperty;
|
||||
TestRegExp;
|
||||
TestRest;
|
||||
TestImportedVar;
|
||||
TestInc2;
|
||||
TestIncDec;
|
||||
TestInlineFunctions;
|
||||
TestInlineFunctions2;
|
||||
TestInnerFunctions;
|
||||
TestInnerFunctionScope;
|
||||
TestInnerIf;
|
||||
TestInnerTry;
|
||||
TestLogicalComputing;
|
||||
TestManualConvert;
|
||||
TestMissingDefault;
|
||||
TestMultipleCondition;
|
||||
TestNamedAnonFunctions;
|
||||
TestNames;
|
||||
TestNegate;
|
||||
TestNumberCall;
|
||||
TestOptionalParameters;
|
||||
TestParamNames;
|
||||
TestParamsCount;
|
||||
TestPrecedence;
|
||||
TestPrecedenceX;
|
||||
TestProperty;
|
||||
TestRegExp;
|
||||
TestRest;
|
||||
TestSlots;
|
||||
TestSlots2;
|
||||
TestStrictEquals;
|
||||
TestStringConcat;
|
||||
TestStrings;
|
||||
TestSwitch;
|
||||
TestSlots2;
|
||||
TestStrictEquals;
|
||||
TestStringConcat;
|
||||
TestStrings;
|
||||
TestSwitch;
|
||||
TestSwitchContinue;
|
||||
TestSwitchComma;
|
||||
TestSwitchDefault;
|
||||
TestSwitchIf;
|
||||
TestTernarOperator;
|
||||
TestTry;
|
||||
TestTryIf;
|
||||
TestTryReturn;
|
||||
TestTryReturn2;
|
||||
TestSwitchComma;
|
||||
TestSwitchDefault;
|
||||
TestSwitchIf;
|
||||
TestTernarOperator;
|
||||
TestTry;
|
||||
TestTryIf;
|
||||
TestTryReturn;
|
||||
TestTryReturn2;
|
||||
TestUndefined;
|
||||
TestUsagesTry;
|
||||
TestVector;
|
||||
TestVector2;
|
||||
TestWhileAnd;
|
||||
TestWhileBreak;
|
||||
TestUsagesTry;
|
||||
TestVector;
|
||||
TestVector2;
|
||||
TestWhileAnd;
|
||||
TestWhileBreak;
|
||||
TestWhileBreak2;
|
||||
TestWhileContinue;
|
||||
TestWhileTry;
|
||||
TestWhileTry2;
|
||||
TestXml;
|
||||
|
||||
SetupMyPackage1;
|
||||
SetupMyPackage2;
|
||||
SetupMyPackage3;
|
||||
|
||||
TestThisOutsideClass;
|
||||
TestImports;
|
||||
TestInitializer;
|
||||
TestRegexpHilight;
|
||||
|
||||
TestPropertyCoerce;
|
||||
TestUnaryMinus;
|
||||
TestWhileContinue;
|
||||
TestWhileTry;
|
||||
TestWhileTry2;
|
||||
TestXml;
|
||||
|
||||
SetupMyPackage1;
|
||||
SetupMyPackage2;
|
||||
SetupMyPackage3;
|
||||
|
||||
TestThisOutsideClass;
|
||||
TestImports;
|
||||
TestInitializer;
|
||||
TestRegexpHilight;
|
||||
|
||||
TestPropertyCoerce;
|
||||
TestUnaryMinus;
|
||||
|
||||
TestModifiers;
|
||||
|
||||
public function Main()
|
||||
{
|
||||
if (stage) init();
|
||||
else addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
}
|
||||
|
||||
private function init(e:Event = null):void
|
||||
{
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
// entry point
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function Main()
|
||||
{
|
||||
if (stage) init();
|
||||
else addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
}
|
||||
|
||||
private function init(e:Event = null):void
|
||||
{
|
||||
removeEventListener(Event.ADDED_TO_STAGE, init);
|
||||
// entry point
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
34
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestDeclarationInterface.as
vendored
Normal file
34
libsrc/ffdec_lib/testdata/as3_new/src/tests/TestDeclarationInterface.as
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package tests
|
||||
{
|
||||
|
||||
public class TestDeclarationInterface
|
||||
{
|
||||
public function run(): MyIFace
|
||||
{
|
||||
var i:MyIFace = null;
|
||||
var n:int = 2;
|
||||
switch(n) {
|
||||
case 0:
|
||||
i = new MyClass();
|
||||
break;
|
||||
case 1:
|
||||
i = new MyClass2();
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface MyIFace {
|
||||
|
||||
}
|
||||
|
||||
class MyClass implements MyIFace {
|
||||
|
||||
}
|
||||
|
||||
class MyClass2 implements MyIFace {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user