google / traceur-compiler

Traceur is a JavaScript.next-to-JavaScript-of-today compiler
Apache License 2.0
8.18k stars 580 forks source link

string literals in super calls prevent obfuscation #1177

Open yankster opened 10 years ago

yankster commented 10 years ago

A "super class" method call is currently translated to a statement like this, containing the "supermethod" name as string literal:

$traceurRuntime.superCall(this, $Superclass.prototype, "supermethod", [...parameters...]);

This breaks support for many popular obfuscators (such as the Google Closure Compiler), because they will rename the method and leave the string literal unchanged.

Is the string literal in the ".superCall" really necessary? Couldn't the compiler translate as follows, and so preserve the method name in the instruction?

$traceurRuntime.methodCall(this, $Superclass.prototype.supermethod, [...parameters...]);

or in plain Prototype manner

$Superclass.prototype.supermethod.call(expanded([...parameters...]));

arv commented 10 years ago

The problem is that we need to walk the prototype chain and look for possible getters.

class B {
  get m() {
    return () => [this, 42];
    }
  }
}

class C extends B {
  m() {
    assert(super()[0] === this);
    assert(super()[1] === 42);
  }
}

I don't see how the above is possible without passing the name as a string.

arv commented 10 years ago

One option would be to have an option to use a non standard super transformer that does not work with accessors. The benefit is that we could use member expression and it would also make the generated code cleaner and faster.

--non-standard-super

class B {
  n() {}
}

class C extends B {
  constructor() {
    super();
  }
  m() {
    super.n();
  }
}

Could use something as simple as:

var B = function B() {};
($traceurRuntime.createClass)(B, {
  n: function() {}
}, {});
var C = function C() {
  B.call(this, ...);
};
var $C = C;
($traceurRuntime.createClass)(C, {
  m: function() {
    B.prototype.n.call(this, ...);
  }
}, {}, B);
arv commented 10 years ago

On Tue, Jul 15, 2014 at 1:14 PM, yankster notifications@github.com wrote:

if(typeof $Superclass.prototype.getsupermethod === "Function")

This would invoke the getter for getsupermethod with $Superclass.prototype as this which would be the wrong semantics.

yankster commented 10 years ago

Thanks for your suggestion. I'm still tossing my hair over your example above :-( Based on their different call syntax (object.property vs. object.method()) I hadn't seen an ancestry relationship (that would require a working super()) between B.m and C.m at all, nor a need for super to mediate between them.

arv commented 10 years ago

A few things to remember:

super() is syntactic sugar for super.prop()

If super.prop is a getter, then that getter needs to be invoked with the right this context.

super.prop() is short for (super.prop).call(this) which should make it clear why we need to invoke a possible getter even in the case where we are doing a super method call.