fix pure_funcs & improve side_effects
- only drops side-effect-free arguments - drop side-effect-free parts with discarded value from `AST_Seq` & `AST_SimpleStatement` closes #1494
This commit is contained in:
178
lib/compress.js
178
lib/compress.js
@@ -83,6 +83,14 @@ function Compressor(options, false_by_default) {
|
|||||||
global_defs : {},
|
global_defs : {},
|
||||||
passes : 1,
|
passes : 1,
|
||||||
}, true);
|
}, true);
|
||||||
|
var pure_funcs = this.options["pure_funcs"];
|
||||||
|
if (typeof pure_funcs == "function") {
|
||||||
|
this.pure_funcs = pure_funcs;
|
||||||
|
} else {
|
||||||
|
this.pure_funcs = pure_funcs ? function(node) {
|
||||||
|
return pure_funcs.indexOf(node.expression.print_to_string()) < 0;
|
||||||
|
} : return_true;
|
||||||
|
}
|
||||||
var top_retain = this.options["top_retain"];
|
var top_retain = this.options["top_retain"];
|
||||||
if (top_retain instanceof RegExp) {
|
if (top_retain instanceof RegExp) {
|
||||||
this.top_retain = function(def) {
|
this.top_retain = function(def) {
|
||||||
@@ -304,6 +312,13 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_iife_call(node) {
|
||||||
|
if (node instanceof AST_Call && !(node instanceof AST_New)) {
|
||||||
|
return node.expression instanceof AST_Function || is_iife_call(node.expression);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function tighten_body(statements, compressor) {
|
function tighten_body(statements, compressor) {
|
||||||
var CHANGED, max_iter = 10;
|
var CHANGED, max_iter = 10;
|
||||||
do {
|
do {
|
||||||
@@ -1354,10 +1369,12 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_This, return_false);
|
def(AST_This, return_false);
|
||||||
|
|
||||||
def(AST_Call, function(compressor){
|
def(AST_Call, function(compressor){
|
||||||
var pure = compressor.option("pure_funcs");
|
if (compressor.pure_funcs(this)) return true;
|
||||||
if (!pure) return true;
|
for (var i = this.args.length; --i >= 0;) {
|
||||||
if (typeof pure == "function") return pure(this);
|
if (this.args[i].has_side_effects(compressor))
|
||||||
return pure.indexOf(this.expression.print_to_string()) < 0;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
def(AST_Block, function(compressor){
|
def(AST_Block, function(compressor){
|
||||||
@@ -1855,12 +1872,151 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// drop_side_effect_free()
|
||||||
|
// remove side-effect-free parts which only affects return value
|
||||||
|
(function(def){
|
||||||
|
function return_this() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function return_null() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop side-effect-free elements from an array of expressions.
|
||||||
|
// Returns an array of expressions with side-effects or null
|
||||||
|
// if all elements were dropped. Note: original array may be
|
||||||
|
// returned if nothing changed.
|
||||||
|
function trim(nodes, compressor, first_in_statement) {
|
||||||
|
var ret = [], changed = false;
|
||||||
|
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||||
|
var node = nodes[i].drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
changed |= node !== nodes[i];
|
||||||
|
if (node) {
|
||||||
|
ret.push(node);
|
||||||
|
first_in_statement = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed ? ret.length ? ret : null : nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
def(AST_Node, return_this);
|
||||||
|
def(AST_Constant, return_null);
|
||||||
|
def(AST_This, return_null);
|
||||||
|
def(AST_Call, function(compressor, first_in_statement){
|
||||||
|
if (compressor.pure_funcs(this)) return this;
|
||||||
|
var args = trim(this.args, compressor, first_in_statement);
|
||||||
|
return args && AST_Seq.from_array(args);
|
||||||
|
});
|
||||||
|
def(AST_Function, return_null);
|
||||||
|
def(AST_Binary, function(compressor, first_in_statement){
|
||||||
|
var right = this.right.drop_side_effect_free(compressor);
|
||||||
|
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
switch (this.operator) {
|
||||||
|
case "&&":
|
||||||
|
case "||":
|
||||||
|
var node = this.clone();
|
||||||
|
node.right = right;
|
||||||
|
return node;
|
||||||
|
default:
|
||||||
|
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
return make_node(AST_Seq, this, {
|
||||||
|
car: left,
|
||||||
|
cdr: right
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
def(AST_Assign, return_this);
|
||||||
|
def(AST_Conditional, function(compressor){
|
||||||
|
var consequent = this.consequent.drop_side_effect_free(compressor);
|
||||||
|
var alternative = this.alternative.drop_side_effect_free(compressor);
|
||||||
|
if (consequent === this.consequent && alternative === this.alternative) return this;
|
||||||
|
if (!consequent) return alternative ? make_node(AST_Binary, this, {
|
||||||
|
operator: "||",
|
||||||
|
left: this.condition,
|
||||||
|
right: alternative
|
||||||
|
}) : this.condition.drop_side_effect_free(compressor);
|
||||||
|
if (!alternative) return make_node(AST_Binary, this, {
|
||||||
|
operator: "&&",
|
||||||
|
left: this.condition,
|
||||||
|
right: consequent
|
||||||
|
});
|
||||||
|
var node = this.clone();
|
||||||
|
node.consequent = consequent;
|
||||||
|
node.alternative = alternative;
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
def(AST_Unary, function(compressor, first_in_statement){
|
||||||
|
switch (this.operator) {
|
||||||
|
case "delete":
|
||||||
|
case "++":
|
||||||
|
case "--":
|
||||||
|
return this;
|
||||||
|
case "typeof":
|
||||||
|
if (this.expression instanceof AST_SymbolRef) return null;
|
||||||
|
default:
|
||||||
|
if (first_in_statement && is_iife_call(this.expression)) return this;
|
||||||
|
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
def(AST_SymbolRef, function() {
|
||||||
|
return this.undeclared() ? this : null;
|
||||||
|
});
|
||||||
|
def(AST_Object, function(compressor, first_in_statement){
|
||||||
|
var values = trim(this.properties, compressor, first_in_statement);
|
||||||
|
return values && AST_Seq.from_array(values);
|
||||||
|
});
|
||||||
|
def(AST_ObjectProperty, function(compressor, first_in_statement){
|
||||||
|
return this.value.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
});
|
||||||
|
def(AST_Array, function(compressor, first_in_statement){
|
||||||
|
var values = trim(this.elements, compressor, first_in_statement);
|
||||||
|
return values && AST_Seq.from_array(values);
|
||||||
|
});
|
||||||
|
def(AST_Dot, function(compressor, first_in_statement){
|
||||||
|
if (!compressor.option("pure_getters")) return this;
|
||||||
|
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
});
|
||||||
|
def(AST_Sub, function(compressor, first_in_statement){
|
||||||
|
if (!compressor.option("pure_getters")) return this;
|
||||||
|
var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
if (!expression) return this.property.drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
var property = this.property.drop_side_effect_free(compressor);
|
||||||
|
if (!property) return expression;
|
||||||
|
return make_node(AST_Seq, this, {
|
||||||
|
car: expression,
|
||||||
|
cdr: property
|
||||||
|
});
|
||||||
|
});
|
||||||
|
def(AST_Seq, function(compressor){
|
||||||
|
var cdr = this.cdr.drop_side_effect_free(compressor);
|
||||||
|
if (cdr === this.cdr) return this;
|
||||||
|
if (!cdr) return this.car;
|
||||||
|
return make_node(AST_Seq, this, {
|
||||||
|
car: this.car,
|
||||||
|
cdr: cdr
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})(function(node, func){
|
||||||
|
node.DEFMETHOD("drop_side_effect_free", func);
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_SimpleStatement, function(self, compressor){
|
OPT(AST_SimpleStatement, function(self, compressor){
|
||||||
if (compressor.option("side_effects")) {
|
if (compressor.option("side_effects")) {
|
||||||
if (!self.body.has_side_effects(compressor)) {
|
var body = self.body;
|
||||||
|
if (!body.has_side_effects(compressor)) {
|
||||||
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_EmptyStatement, self);
|
return make_node(AST_EmptyStatement, self);
|
||||||
}
|
}
|
||||||
|
var node = body.drop_side_effect_free(compressor, true);
|
||||||
|
if (!node) {
|
||||||
|
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
||||||
|
return make_node(AST_EmptyStatement, self);
|
||||||
|
}
|
||||||
|
if (node !== body) {
|
||||||
|
return make_node(AST_SimpleStatement, self, { body: node });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
@@ -2435,13 +2591,6 @@ merge(Compressor.prototype, {
|
|||||||
return self.negate(compressor, true);
|
return self.negate(compressor, true);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
function is_iife_call(node) {
|
|
||||||
if (node instanceof AST_Call && !(node instanceof AST_New)) {
|
|
||||||
return node.expression instanceof AST_Function || is_iife_call(node.expression);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_New, function(self, compressor){
|
OPT(AST_New, function(self, compressor){
|
||||||
@@ -2464,9 +2613,8 @@ merge(Compressor.prototype, {
|
|||||||
OPT(AST_Seq, function(self, compressor){
|
OPT(AST_Seq, function(self, compressor){
|
||||||
if (!compressor.option("side_effects"))
|
if (!compressor.option("side_effects"))
|
||||||
return self;
|
return self;
|
||||||
if (!self.car.has_side_effects(compressor)) {
|
self.car = self.car.drop_side_effect_free(compressor, first_in_statement(compressor));
|
||||||
return maintain_this_binding(compressor.parent(), self, self.cdr);
|
if (!self.car) return maintain_this_binding(compressor.parent(), self, self.cdr);
|
||||||
}
|
|
||||||
if (compressor.option("cascade")) {
|
if (compressor.option("cascade")) {
|
||||||
if (self.car instanceof AST_Assign
|
if (self.car instanceof AST_Assign
|
||||||
&& !self.car.left.has_side_effects(compressor)) {
|
&& !self.car.left.has_side_effects(compressor)) {
|
||||||
|
|||||||
@@ -590,3 +590,61 @@ drop_fnames: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global_var: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
function foo(b) {
|
||||||
|
a;
|
||||||
|
b;
|
||||||
|
c;
|
||||||
|
typeof c === "undefined";
|
||||||
|
c + b + a;
|
||||||
|
b && b.ar();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
function foo(b) {
|
||||||
|
c;
|
||||||
|
c;
|
||||||
|
b && b.ar();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iife: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
var a;
|
||||||
|
~function() {}(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
~function() {}(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_value: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(1, [2, foo()], 3, {a:1, b:bar()});
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo(), bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
295
test/compress/pure_funcs.js
Normal file
295
test/compress/pure_funcs.js
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
array: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "Math.floor" ],
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
function f(b) {
|
||||||
|
Math.floor(a / b);
|
||||||
|
Math.floor(c / b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
function f(b) {
|
||||||
|
c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: function(node) {
|
||||||
|
return !~node.args[0].print_to_string().indexOf("a");
|
||||||
|
},
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a, b) {
|
||||||
|
Math.floor(a / b);
|
||||||
|
Math.floor(c / b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a, b) {
|
||||||
|
Math.floor(c / b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
side_effects: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "console.log" ],
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a, b) {
|
||||||
|
console.log(a());
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a, b) {
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "pure" ],
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
var u = pure(1);
|
||||||
|
var x = pure(2);
|
||||||
|
var y = pure(x);
|
||||||
|
var z = pure(pure(side_effects()));
|
||||||
|
return pure(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
side_effects();
|
||||||
|
return pure(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
babel: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "_classCallCheck" ],
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function _classCallCheck(instance, Constructor) {
|
||||||
|
if (!(instance instanceof Constructor))
|
||||||
|
throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
|
var Foo = function Foo() {
|
||||||
|
_classCallCheck(this, Foo);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function _classCallCheck(instance, Constructor) {
|
||||||
|
if (!(instance instanceof Constructor))
|
||||||
|
throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
|
var Foo = function() {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conditional: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "pure" ],
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
pure(1 | a() ? 2 & b() : 7 ^ c());
|
||||||
|
pure(1 | a() ? 2 & b() : 5);
|
||||||
|
pure(1 | a() ? 4 : 7 ^ c());
|
||||||
|
pure(1 | a() ? 4 : 5);
|
||||||
|
pure(3 ? 2 & b() : 7 ^ c());
|
||||||
|
pure(3 ? 2 & b() : 5);
|
||||||
|
pure(3 ? 4 : 7 ^ c());
|
||||||
|
pure(3 ? 4 : 5);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
1 | a() ? b() : c();
|
||||||
|
1 | a() && b();
|
||||||
|
1 | a() || c();
|
||||||
|
a();
|
||||||
|
3 ? b() : c();
|
||||||
|
3 && b();
|
||||||
|
3 || c();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relational: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "foo" ],
|
||||||
|
side_effects :true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
foo() in foo();
|
||||||
|
foo() instanceof bar();
|
||||||
|
foo() < "bar";
|
||||||
|
bar() > foo();
|
||||||
|
bar() != bar();
|
||||||
|
bar() !== "bar";
|
||||||
|
"bar" == foo();
|
||||||
|
"bar" === bar();
|
||||||
|
"bar" >= "bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
bar();
|
||||||
|
bar(), bar();
|
||||||
|
bar();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arithmetic: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "foo" ],
|
||||||
|
side_effects :true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
foo() + foo();
|
||||||
|
foo() - bar();
|
||||||
|
foo() * "bar";
|
||||||
|
bar() / foo();
|
||||||
|
bar() & bar();
|
||||||
|
bar() | "bar";
|
||||||
|
"bar" >> foo();
|
||||||
|
"bar" << bar();
|
||||||
|
"bar" >>> "bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
bar();
|
||||||
|
bar(), bar();
|
||||||
|
bar();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_and: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "foo" ],
|
||||||
|
side_effects :true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
foo() && foo();
|
||||||
|
foo() && bar();
|
||||||
|
foo() && "bar";
|
||||||
|
bar() && foo();
|
||||||
|
bar() && bar();
|
||||||
|
bar() && "bar";
|
||||||
|
"bar" && foo();
|
||||||
|
"bar" && bar();
|
||||||
|
"bar" && "bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo() && bar();
|
||||||
|
bar();
|
||||||
|
bar() && bar();
|
||||||
|
bar();
|
||||||
|
"bar" && bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_or: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "foo" ],
|
||||||
|
side_effects :true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
foo() || foo();
|
||||||
|
foo() || bar();
|
||||||
|
foo() || "bar";
|
||||||
|
bar() || foo();
|
||||||
|
bar() || bar();
|
||||||
|
bar() || "bar";
|
||||||
|
"bar" || foo();
|
||||||
|
"bar" || bar();
|
||||||
|
"bar" || "bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo() || bar();
|
||||||
|
bar();
|
||||||
|
bar() || bar();
|
||||||
|
bar();
|
||||||
|
"bar" || bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assign: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "foo" ],
|
||||||
|
side_effects :true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
function f(b) {
|
||||||
|
a = foo();
|
||||||
|
b *= 4 + foo();
|
||||||
|
c >>= 0 | foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
function f(b) {
|
||||||
|
a = foo();
|
||||||
|
b *= 4 + foo();
|
||||||
|
c >>= 0 | foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unary: {
|
||||||
|
options = {
|
||||||
|
pure_funcs: [ "foo" ],
|
||||||
|
side_effects :true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
typeof foo();
|
||||||
|
typeof bar();
|
||||||
|
typeof "bar";
|
||||||
|
void foo();
|
||||||
|
void bar();
|
||||||
|
void "bar";
|
||||||
|
delete a[foo()];
|
||||||
|
delete a[bar()];
|
||||||
|
delete a["bar"];
|
||||||
|
a[foo()]++;
|
||||||
|
a[bar()]++;
|
||||||
|
a["bar"]++;
|
||||||
|
--a[foo()];
|
||||||
|
--a[bar()];
|
||||||
|
--a["bar"];
|
||||||
|
~foo();
|
||||||
|
~bar();
|
||||||
|
~"bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
bar();
|
||||||
|
delete a[foo()];
|
||||||
|
delete a[bar()];
|
||||||
|
delete a["bar"];
|
||||||
|
a[foo()]++;
|
||||||
|
a[bar()]++;
|
||||||
|
a["bar"]++;
|
||||||
|
--a[foo()];
|
||||||
|
--a[bar()];
|
||||||
|
--a["bar"];
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user