]> git.saurik.com Git - cycript.git/blobdiff - Replace.cpp
Simplify command return by avoiding extra newline.
[cycript.git] / Replace.cpp
index 74f04d9d489f7712d48366cd7837dc44ca35a32e..ac673adefd204c04966a66d0a51f0f93b01cce0e 100644 (file)
 **/
 /* }}} */
 
-#include "Parser.hpp"
-#include "Replace.hpp"
-
 #include <iomanip>
 
+#include "Replace.hpp"
+#include "Syntax.hpp"
+
 CYFunctionExpression *CYNonLocalize(CYContext &context, CYFunctionExpression *function) {
     function->nonlocal_ = context.nextlocal_;
     return function;
 }
 
+CYFunctionExpression *CYSuperize(CYContext &context, CYFunctionExpression *function) {
+    function->super_ = context.super_;
+    return function;
+}
+
+CYStatement *CYDefineProperty(CYExpression *object, CYExpression *name, bool configurable, bool enumerable, CYProperty *descriptor) {
+    return $E($C3($M($V("Object"), $S("defineProperty")), object, name, $ CYObject(CYList<CYProperty>()
+        ->* (configurable ? $ CYPropertyValue($S("configurable"), $ CYTrue()) : NULL)
+        ->* (enumerable ? $ CYPropertyValue($S("enumerable"), $ CYTrue()) : NULL)
+        ->* descriptor)));
+}
+
+static void CYImplicitReturn(CYStatement *&code) {
+    if (CYStatement *&last = CYGetLast(code))
+        last = last->Return();
+}
+
 CYExpression *CYAdd::Replace(CYContext &context) {
     CYInfix::Replace(context);
 
@@ -75,14 +92,15 @@ CYArgument *CYArgument::Replace(CYContext &context) { $T(NULL)
 }
 
 CYExpression *CYArray::Replace(CYContext &context) {
-    elements_->Replace(context);
+    if (elements_ != NULL)
+        elements_->Replace(context);
     return this;
 }
 
 CYExpression *CYArrayComprehension::Replace(CYContext &context) {
     CYVariable *cyv($V("$cyv"));
 
-    return $C0($F(NULL, $P1($L("$cyv"), comprehensions_->Parameters(context)), $$->*
+    return $C0($F(NULL, $P1($L($I("$cyv")), comprehensions_->Parameters(context)), $$->*
         $E($ CYAssign(cyv, $ CYArray()))->*
         comprehensions_->Replace(context, $E($C1($M(cyv, $S("push")), expression_)))->*
         $ CYReturn(cyv)
@@ -95,6 +113,11 @@ CYExpression *CYAssignment::Replace(CYContext &context) {
     return this;
 }
 
+CYStatement *CYBlock::Return() {
+    CYImplicitReturn(code_);
+    return this;
+}
+
 CYStatement *CYBlock::Replace(CYContext &context) {
     context.ReplaceAll(code_);
     if (code_ == NULL)
@@ -135,16 +158,51 @@ void Catch::Replace(CYContext &context) { $T()
 
 } }
 
+CYExpression *CYClassExpression::Replace(CYContext &context) {
+    CYBuilder builder;
+
+    CYIdentifier *super(context.Unique());
+
+    CYIdentifier *old(context.super_);
+    context.super_ = super;
+
+    CYIdentifier *constructor(context.Unique());
+    CYForEach (member, tail_->static_)
+        member->Replace(context, builder, $V(constructor), true);
+
+    CYIdentifier *prototype(context.Unique());
+    CYForEach (member, tail_->instance_)
+        member->Replace(context, builder, $V(prototype), true);
+
+    if (tail_->constructor_ == NULL)
+        tail_->constructor_ = $ CYFunctionExpression(NULL, NULL, NULL);
+    tail_->constructor_ = CYSuperize(context, tail_->constructor_);
+
+    context.super_ = old;
+
+    return $C1($ CYFunctionExpression(NULL, $P($L(super)), $$
+        ->* $ CYVar($L1($L(constructor, tail_->constructor_)))
+        ->* $ CYVar($L1($L(prototype, $ CYFunctionExpression(NULL, NULL, NULL))))
+        ->* $E($ CYAssign($M($V(prototype), $S("prototype")), $M($V(super), $S("prototype"))))
+        ->* $E($ CYAssign($V(prototype), $N($V(prototype))))
+        ->* CYDefineProperty($V(prototype), $S("constructor"), false, false, $ CYPropertyValue($S("value"), $V(constructor)))
+        ->* $ CYVar(builder.declarations_)
+        ->* builder.statements_
+        ->* CYDefineProperty($V(constructor), $S("prototype"), false, false, $ CYPropertyValue($S("value"), $V(prototype)))
+        ->* $ CYReturn($V(constructor))
+    ), tail_->extends_ ?: $V($I("Object")));
+}
+
+CYStatement *CYClassStatement::Replace(CYContext &context) {
+    return $ CYVar($L1($L(name_, $ CYClassExpression(name_, tail_))));
+}
+
 void CYClause::Replace(CYContext &context) { $T()
     context.Replace(case_);
     context.ReplaceAll(code_);
     next_->Replace(context);
 }
 
-CYStatement *CYComment::Replace(CYContext &context) {
-    return this;
-}
-
 CYExpression *CYCompound::Replace(CYContext &context) {
     context.Replace(expression_);
     context.Replace(next_);
@@ -185,6 +243,10 @@ CYStatement *CYComprehension::Replace(CYContext &context, CYStatement *statement
     return next_ == NULL ? statement : next_->Replace(context, statement);
 }
 
+CYExpression *CYComputed::PropertyName(CYContext &context) {
+    return expression_;
+}
+
 CYExpression *CYCondition::Replace(CYContext &context) {
     context.Replace(test_);
     context.Replace(true_);
@@ -200,7 +262,7 @@ void CYContext::NonLocal(CYStatement *&statements) {
         CYIdentifier *unique(nextlocal_->identifier_->Replace(context));
 
         CYStatement *declare(
-            $ CYVar($L1($ CYDeclaration(unique, $ CYObject()))));
+            $ CYVar($L1($L(unique, $ CYObject()))));
 
         cy::Syntax::Catch *rescue(
             $ cy::Syntax::Catch(cye, $$->*
@@ -258,10 +320,6 @@ void CYDeclarations::Replace(CYContext &context) { $T()
     next_->Replace(context);
 }
 
-CYProperty *CYDeclarations::Property(CYContext &context) { $T(NULL)
-    return $ CYProperty(declaration_->identifier_, declaration_->initialiser_, next_->Property(context));
-}
-
 CYFunctionParameter *CYDeclarations::Parameter(CYContext &context) { $T(NULL)
     return $ CYFunctionParameter($ CYDeclaration(declaration_->identifier_), next_->Parameter(context));
 }
@@ -292,9 +350,14 @@ CYStatement *CYDoWhile::Replace(CYContext &context) {
     return this;
 }
 
-void CYElement::Replace(CYContext &context) { $T()
+void CYElementSpread::Replace(CYContext &context) {
     context.Replace(value_);
-    next_->Replace(context);
+}
+
+void CYElementValue::Replace(CYContext &context) {
+    context.Replace(value_);
+    if (next_ != NULL)
+        next_->Replace(context);
 }
 
 CYStatement *CYEmpty::Replace(CYContext &context) {
@@ -305,6 +368,10 @@ CYExpression *CYEncodedType::Replace(CYContext &context) {
     return typed_->Replace(context);
 }
 
+CYStatement *CYExpress::Return() {
+    return $ CYReturn(expression_);
+}
+
 CYStatement *CYExpress::Replace(CYContext &context) {
     context.Replace(expression_);
     return this;
@@ -314,10 +381,6 @@ CYExpression *CYExpression::AddArgument(CYContext &context, CYExpression *value)
     return $C1(this, value);
 }
 
-CYExpression *CYExpression::ClassName(CYContext &context, bool object) {
-    return this;
-}
-
 CYStatement *CYExpression::ForEachIn(CYContext &context, CYExpression *value) {
     return $E($ CYAssign(this, value));
 }
@@ -380,11 +443,11 @@ CYStatement *CYForIn::Replace(CYContext &context) {
 }
 
 CYFunctionParameter *CYForInComprehension::Parameter(CYContext &context) const {
-    return $ CYFunctionParameter($ CYDeclaration(name_));
+    return $ CYFunctionParameter(declaration_);
 }
 
 CYStatement *CYForInComprehension::Replace(CYContext &context, CYStatement *statement) const {
-    return $ CYForIn($V(name_), set_, CYComprehension::Replace(context, statement));
+    return $ CYForIn(declaration_->Variable(context), set_, CYComprehension::Replace(context, statement));
 }
 
 CYStatement *CYForOf::Replace(CYContext &context) {
@@ -394,43 +457,39 @@ CYStatement *CYForOf::Replace(CYContext &context) {
             this
         );
 
-    CYIdentifier *cys($I("$cys")), *cyt($I("$cyt"));
+    CYIdentifier *cys(context.Unique()), *cyt(context.Unique());
 
-    return $ CYLetStatement($L2($ CYDeclaration(cys, set_), $ CYDeclaration(cyt)), $$->*
-        $ CYForIn($V(cyt), $V(cys), $ CYBlock($$->*
-            initialiser_->ForEachIn(context, $M($V(cys), $V(cyt)))->*
-            code_
-        ))
-    );
+    return $ CYBlock($$
+        ->* $ CYLet($L2($L(cys, set_), $L(cyt)))
+        ->* $ CYForIn($V(cyt), $V(cys), $ CYBlock($$
+            ->* initialiser_->ForEachIn(context, $M($V(cys), $V(cyt)))
+            ->* code_
+    )));
 }
 
 CYFunctionParameter *CYForOfComprehension::Parameter(CYContext &context) const {
-    return $ CYFunctionParameter($ CYDeclaration(name_));
+    return $ CYFunctionParameter(declaration_);
 }
 
 CYStatement *CYForOfComprehension::Replace(CYContext &context, CYStatement *statement) const {
     CYIdentifier *cys($I("$cys"));
 
-    return $E($C0($F(NULL, $P1($L("$cys")), $$->*
+    return $E($C0($F(NULL, $P1($L($I("$cys"))), $$->*
         $E($ CYAssign($V(cys), set_))->*
-        $ CYForIn($V(name_), $V(cys), $ CYBlock($$->*
-            $E($ CYAssign($V(name_), $M($V(cys), $V(name_))))->*
+        $ CYForIn(declaration_->Variable(context), $V(cys), $ CYBlock($$->*
+            $E($ CYAssign(declaration_->Variable(context), $M($V(cys), declaration_->Variable(context))))->*
             CYComprehension::Replace(context, statement)
         ))
     )));
 }
 
-void CYFunction::Inject(CYContext &context) {
-    context.Replace(name_);
-    context.scope_->Declare(context, name_, CYIdentifierOther);
-}
-
-void CYFunction::Replace_(CYContext &context, bool outer) {
-    if (outer)
-        Inject(context);
-
+void CYFunction::Replace(CYContext &context) {
     CYThisScope *_this(context.this_);
-    context.this_ = CYGetLast(&this_);
+    context.this_ = &this_;
+    context.this_ = CYGetLast(context.this_);
+
+    CYIdentifier *super(context.super_);
+    context.super_ = super_;
 
     CYNonLocal *nonlocal(context.nonlocal_);
     CYNonLocal *nextlocal(context.nextlocal_);
@@ -447,16 +506,20 @@ void CYFunction::Replace_(CYContext &context, bool outer) {
 
     CYScope scope(!localize, context);
 
-    if (!outer && name_ != NULL)
-        Inject(context);
-
     parameters_->Replace(context, code_);
+
     context.ReplaceAll(code_);
 
-    if (CYIdentifier *identifier = this_.identifier_)
-        code_ = $$->*
-            $ CYVar($L1($ CYDeclaration(identifier, $ CYThis())))->*
-            code_;
+    if (implicit_)
+        CYImplicitReturn(code_);
+
+    if (CYIdentifier *identifier = this_.identifier_) {
+        context.scope_->Declare(context, identifier, CYIdentifierVariable);
+        code_ = $$
+            ->*$E($ CYAssign($V(identifier), $ CYThis()))
+            ->*code_
+        ;
+    }
 
     if (localize)
         context.NonLocal(code_);
@@ -464,13 +527,18 @@ void CYFunction::Replace_(CYContext &context, bool outer) {
     context.nextlocal_ = nextlocal;
     context.nonlocal_ = nonlocal;
 
+    context.super_ = super;
     context.this_ = _this;
 
     scope.Close(context, code_);
 }
 
 CYExpression *CYFunctionExpression::Replace(CYContext &context) {
-    Replace_(context, false);
+    CYScope scope(false, context);
+    if (name_ != NULL)
+        context.scope_->Declare(context, name_, CYIdentifierOther);
+    CYFunction::Replace(context);
+    scope.Close(context, code_);
     return this;
 }
 
@@ -490,7 +558,8 @@ void CYFunctionParameter::Replace(CYContext &context, CYStatement *&statements)
 }
 
 CYStatement *CYFunctionStatement::Replace(CYContext &context) {
-    Replace_(context, true);
+    context.scope_->Declare(context, name_, CYIdentifierOther);
+    CYFunction::Replace(context);
     return this;
 }
 
@@ -501,6 +570,12 @@ CYIdentifier *CYIdentifier::Replace(CYContext &context) {
     return replace_;
 }
 
+CYStatement *CYIf::Return() {
+    CYImplicitReturn(true_);
+    CYImplicitReturn(false_);
+    return this;
+}
+
 CYStatement *CYIf::Replace(CYContext &context) {
     context.Replace(test_);
     context.ReplaceAll(true_);
@@ -517,7 +592,7 @@ CYStatement *CYIfComprehension::Replace(CYContext &context, CYStatement *stateme
 }
 
 CYStatement *CYImport::Replace(CYContext &context) {
-    return $ CYVar($L1($L(module_->part_->Word(), $C1($V("require"), module_->Replace(context, "/")))));
+    return $ CYVar($L1($L($I(module_->part_->Word()), $C1($V("require"), module_->Replace(context, "/")))));
 }
 
 CYExpression *CYIndirect::Replace(CYContext &context) {
@@ -543,8 +618,19 @@ CYExpression *CYLambda::Replace(CYContext &context) {
     return $N2($V("Functor"), $ CYFunctionExpression(NULL, parameters_->Parameters(context), code_), parameters_->TypeSignature(context, typed_->Replace(context)));
 }
 
-CYStatement *CYLetStatement::Replace(CYContext &context) {
-    return $E($ CYCall(CYNonLocalize(context, $ CYFunctionExpression(NULL, declarations_->Parameter(context), code_)), declarations_->Argument(context)));
+CYStatement *CYLet::Replace(CYContext &context) {
+    declarations_->Replace(context);
+    if (CYExpression *expression = declarations_->Expression(context))
+        return $E(expression);
+    return $ CYEmpty();
+}
+
+CYFunctionExpression *CYMethod::Constructor() {
+    return NULL;
+}
+
+void CYMethod::Replace(CYContext &context) {
+    CYFunction::Replace(context);
 }
 
 CYString *CYModule::Replace(CYContext &context, const char *separator) const {
@@ -596,8 +682,24 @@ CYString *CYNumber::String(CYContext &context) {
     return $S($pool.sprintf(24, "%.17g", Value()));
 }
 
+CYExpression *CYNumber::PropertyName(CYContext &context) {
+    return String(context);
+}
+
 CYExpression *CYObject::Replace(CYContext &context) {
-    properties_->Replace(context);
+    CYBuilder builder;
+    if (properties_ != NULL)
+        properties_ = properties_->ReplaceAll(context, builder, $ CYThis(), false);
+
+    if (builder) {
+        return $C1($M($ CYFunctionExpression(NULL, builder.declarations_->Parameter(context),
+            builder.statements_->*
+                $ CYReturn($ CYThis())
+        ), $S("call")), this, builder.declarations_->Argument(context));
+    }
+
+    CYForEach (property, properties_)
+        property->Replace(context);
     return this;
 }
 
@@ -615,6 +717,68 @@ CYExpression *CYPrefix::Replace(CYContext &context) {
     return this;
 }
 
+CYProperty *CYProperty::ReplaceAll(CYContext &context, CYBuilder &builder, CYExpression *self, bool update) {
+    update |= name_->Computed();
+    if (update)
+        Replace(context, builder, self, false);
+    if (next_ != NULL)
+        next_ = next_->ReplaceAll(context, builder, self, update);
+    return update ? next_ : this;
+}
+
+void CYProperty::Replace(CYContext &context, CYBuilder &builder, CYExpression *self, bool protect) {
+    CYExpression *name(name_->PropertyName(context));
+    if (name_->Computed()) {
+        CYIdentifier *unique(context.Unique());
+        builder.declarations_->*$L1($L(unique, name));
+        name = $V(unique);
+    }
+
+    Replace(context, builder, self, name, protect);
+}
+
+void CYPropertyGetter::Replace(CYContext &context, CYBuilder &builder, CYExpression *self, CYExpression *name, bool protect) {
+    CYIdentifier *unique(context.Unique());
+    builder.declarations_
+        ->* $L1($L(unique, CYSuperize(context, $ CYFunctionExpression(NULL, parameters_, code_))));
+    builder.statements_
+        ->* CYDefineProperty(self, name, true, !protect, $ CYPropertyValue($S("get"), $V(unique)));
+}
+
+CYFunctionExpression *CYPropertyMethod::Constructor() {
+    return name_->Constructor() ? $ CYFunctionExpression(NULL, parameters_, code_) : NULL;
+}
+
+void CYPropertyMethod::Replace(CYContext &context, CYBuilder &builder, CYExpression *self, CYExpression *name, bool protect) {
+    CYIdentifier *unique(context.Unique());
+    builder.declarations_
+        ->* $L1($L(unique, CYSuperize(context, $ CYFunctionExpression(NULL, parameters_, code_))));
+    builder.statements_
+        ->* (!protect ? $E($ CYAssign($M(self, name), $V(unique))) :
+            CYDefineProperty(self, name, true, !protect, $ CYPropertyValue($S("value"), $V(unique), $ CYPropertyValue($S("writable"), $ CYTrue()))));
+}
+
+void CYPropertySetter::Replace(CYContext &context, CYBuilder &builder, CYExpression *self, CYExpression *name, bool protect) {
+    CYIdentifier *unique(context.Unique());
+    builder.declarations_
+        ->* $L1($L(unique, CYSuperize(context, $ CYFunctionExpression(NULL, parameters_, code_))));
+    builder.statements_
+        ->* CYDefineProperty(self, name, true, !protect, $ CYPropertyValue($S("set"), $V(unique)));
+}
+
+void CYPropertyValue::Replace(CYContext &context, CYBuilder &builder, CYExpression *self, CYExpression *name, bool protect) {
+    _assert(!protect);
+    CYIdentifier *unique(context.Unique());
+    builder.declarations_
+        ->* $L1($L(unique, value_));
+    builder.statements_
+        ->* $E($ CYAssign($M(self, name), $V(unique)));
+}
+
+void CYPropertyValue::Replace(CYContext &context) {
+    context.Replace(value_);
+}
+
 // XXX: this is evil evil black magic. don't ask, don't tell... don't believe!
 #define MappingSet "0etnirsoalfucdphmgyvbxTwSNECAFjDLkMOIBPqzRH$_WXUVGYKQJZ"
 //#define MappingSet "0abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"
@@ -633,7 +797,7 @@ namespace {
     typedef std::set<CYIdentifier *, IdentifierUsageLess> IdentifierUsages;
 }
 
-void CYProgram::Replace(CYContext &context) {
+void CYScript::Replace(CYContext &context) {
     CYScope scope(true, context);
 
     context.nextlocal_ = $ CYNonLocal();
@@ -689,18 +853,11 @@ void CYProgram::Replace(CYContext &context) {
     }
 }
 
-void CYProperty::Replace(CYContext &context) { $T()
-    context.Replace(value_);
-    next_->Replace(context);
-    if (value_ == NULL)
-        value_ = $U;
-}
-
 CYStatement *CYReturn::Replace(CYContext &context) {
     if (context.nonlocal_ != NULL) {
-        CYProperty *value(value_ == NULL ? NULL : $ CYProperty($S("$cyv"), value_));
+        CYProperty *value(value_ == NULL ? NULL : $ CYPropertyValue($S("$cyv"), value_));
         return $ cy::Syntax::Throw($ CYObject(
-            $ CYProperty($S("$cyk"), $V(context.nonlocal_->Target(context)), value)
+            $ CYPropertyValue($S("$cyk"), $V(context.nonlocal_->Target(context)), value)
         ));
     }
 
@@ -709,12 +866,18 @@ CYStatement *CYReturn::Replace(CYContext &context) {
 }
 
 CYExpression *CYRubyBlock::Replace(CYContext &context) {
-    // XXX: this needs to do something much more epic to handle return
     return call_->AddArgument(context, proc_->Replace(context));
 }
 
+CYExpression *CYRubyBlock::AddArgument(CYContext &context, CYExpression *value) {
+    return Replace(context)->AddArgument(context, value);
+}
+
 CYExpression *CYRubyProc::Replace(CYContext &context) {
-    return CYNonLocalize(context, $ CYFunctionExpression(NULL, parameters_, code_));
+    CYFunctionExpression *function($ CYFunctionExpression(NULL, parameters_, code_));
+    function = CYNonLocalize(context, function);
+    function->implicit_ = true;
+    return function;
 }
 
 CYScope::CYScope(bool transparent, CYContext &context) :
@@ -831,6 +994,14 @@ void CYScope::Close(CYContext &context, CYStatement *&statements) {
         }
 }
 
+CYElementValue *CYSpan::Replace(CYContext &context) { $T(NULL)
+    return $ CYElementValue(expression_, $ CYElementValue(string_, next_->Replace(context)));
+}
+
+CYStatement *CYStatement::Return() {
+    return this;
+}
+
 CYString *CYString::Concat(CYContext &context, CYString *rhs) const {
     size_t size(size_ + rhs->size_);
     char *value($ char[size + 1]);
@@ -845,16 +1016,32 @@ CYNumber *CYString::Number(CYContext &context) {
     return NULL;
 }
 
+CYExpression *CYString::PropertyName(CYContext &context) {
+    return this;
+}
+
 CYString *CYString::String(CYContext &context) {
     return this;
 }
 
+CYExpression *CYSuperAccess::Replace(CYContext &context) {
+    return $C1($M($M($M($V(context.super_), $S("prototype")), property_), $S("bind")), $ CYThis());
+}
+
+CYExpression *CYSuperCall::Replace(CYContext &context) {
+    return $C($C1($M($V(context.super_), $S("bind")), $ CYThis()), arguments_);
+}
+
 CYStatement *CYSwitch::Replace(CYContext &context) {
     context.Replace(value_);
     clauses_->Replace(context);
     return this;
 }
 
+CYExpression *CYTemplate::Replace(CYContext &context) {
+    return $C2($M($M($M($V("String"), $S("prototype")), $S("concat")), $S("apply")), $S(""), $ CYArray($ CYElementValue(string_, spans_->Replace(context))));
+}
+
 CYExpression *CYThis::Replace(CYContext &context) {
     if (context.this_ != NULL)
         return $V(context.this_->Identifier(context));
@@ -1013,10 +1200,6 @@ CYStatement *CYWith::Replace(CYContext &context) {
     return this;
 }
 
-CYExpression *CYWord::ClassName(CYContext &context, bool object) {
-    CYString *name($S(this));
-    if (object)
-        return $C1($V("objc_getClass"), name);
-    else
-        return name;
+CYExpression *CYWord::PropertyName(CYContext &context) {
+    return $S(this);
 }