diff --git a/src/context3d.cpp b/src/context3d.cpp
index 1ed9d686dafc9586e346f604a2d4aabe124d2478..4b4acf3f6017d9913d32d34cba6678d313292e0d 100644
--- a/src/context3d.cpp
+++ b/src/context3d.cpp
@@ -1691,7 +1691,7 @@ void CanvasContext::deleteProgram(CanvasProgram *program)
 
 /*!
  * \qmlmethod void Context3D::attachShader(Program3D program, Shader3D shader)
- * Deletes the given program as if by calling \c{glDeleteProgram()}.
+ * Attaches the given \a shader object to the given \a program object.
  * Calling this method repeatedly on the same object has no side effects.
  * \a program is the Program3D to be deleted.
  */
@@ -1704,9 +1704,37 @@ void CanvasContext::attachShader(CanvasProgram *program, CanvasShader *shader)
     if (!program || !shader || !program->isAlive())
         return;
 
-    program->qOGLProgram()->addShader(shader->qOGLShader());
+    program->attach(shader);
+}
+
+/*!
+ * \qmlmethod list<Shader3D> Context3D::getAttachedShaders(Program3D program)
+ * Returns the list of shaders currently attached to the given \a program.
+ */
+/*!
+ * \internal
+ */
+QVariantList CanvasContext::getAttachedShaders(CanvasProgram *program)
+{
+    if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__
+                                << "(program:" << program << ")";
+
+    QVariantList shaderList;
+    if (!program)
+        return shaderList;
+
+    QList<CanvasShader *> shaders = program->attachedShaders();
+
+    for (QList<CanvasShader *>::const_iterator iter = shaders.constBegin();
+         iter != shaders.constEnd(); iter++) {
+        CanvasShader *shader = *iter;
+        shaderList << QVariant::fromValue(shader);
+    }
+
+    return shaderList;
 }
 
+
 /*!
  * \qmlmethod void Context3D::detachShader(Program3D program, Shader3D shader)
  * Detaches given shader object from given program object.
@@ -1722,7 +1750,7 @@ void CanvasContext::detachShader(CanvasProgram *program, CanvasShader *shader)
     if (!program || !shader || !program->isAlive())
         return;
 
-    program->qOGLProgram()->removeShader(shader->qOGLShader());
+    program->detach(shader);
 }
 
 /*!
@@ -1739,7 +1767,7 @@ void CanvasContext::linkProgram(CanvasProgram *program)
     if (!program || !program->isAlive())
         return;
 
-    program->qOGLProgram()->link();
+    program->link();
 }
 
 /*!
@@ -2025,7 +2053,7 @@ QVariant CanvasContext::getProgramParameter(CanvasProgram *program, glEnums para
         // Intentional flow through
     case VALIDATE_STATUS: {
         GLint value = 0;
-        glGetProgramiv(program->qOGLProgram()->programId(), GLenum(paramName), &value);
+        glGetProgramiv(program->id(), GLenum(paramName), &value);
         if (m_logAllCalls) qDebug() << "    getProgramParameter returns " << value;
         return QVariant::fromValue(value == GL_TRUE);
     }
@@ -2035,7 +2063,7 @@ QVariant CanvasContext::getProgramParameter(CanvasProgram *program, glEnums para
         // Intentional flow through
     case ACTIVE_UNIFORMS: {
         GLint value = 0;
-        glGetProgramiv(program->qOGLProgram()->programId(), GLenum(paramName), &value);
+        glGetProgramiv(program->id(), GLenum(paramName), &value);
         if (m_logAllCalls) qDebug() << "    getProgramParameter returns " << value;
         return QVariant::fromValue(value);
     }
@@ -2762,7 +2790,7 @@ CanvasUniformLocation *CanvasContext::getUniformLocation(CanvasProgram *program,
         return 0;
     }
 
-    int index = program->qOGLProgram()->uniformLocation(name);
+    int index = program->uniformLocation(name);
     if (index < 0) {
         //if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ": INVALID name " << name;
         return 0;
@@ -2791,10 +2819,10 @@ int CanvasContext::getAttribLocation(CanvasProgram *program, const QString &name
         if (m_logAllErrors) qDebug() << "Context3D::" << __FUNCTION__ << ": INVALID Program3D reference " << program;
         return -1;
     } else {
-        if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(" <<program<< ", " <<name << "):" << program->qOGLProgram()->attributeLocation(name);
+        if (m_logAllCalls) qDebug() << "Context3D::" << __FUNCTION__ << "(" <<program<< ", " <<name << "):" << program->attributeLocation(name);
     }
 
-    return program->qOGLProgram()->attributeLocation(name);
+    return program->attributeLocation(name);
 }
 
 /*!
@@ -2812,7 +2840,7 @@ void CanvasContext::bindAttribLocation(CanvasProgram *program, int index, const
         return;
     }
 
-    program->qOGLProgram()->bindAttributeLocation(name, index);
+    program->bindAttributeLocation(index, name);
 }
 
 /*!
@@ -3420,7 +3448,7 @@ QString CanvasContext::getProgramInfoLog(CanvasProgram *program) const
         return QString();
 
     // Returning a copy, V4VM takes ownership
-    return program->qOGLProgram()->log();
+    return program->log();
 }
 
 /*!
@@ -3502,9 +3530,9 @@ void CanvasContext::useProgram(CanvasProgram *program)
                                 << "(program:" << program << ")";
     // TODO: Check if this is ok
     m_currentProgram = program;
-    if (!program || !program->qOGLProgram()->isLinked())
+    if (!program || !program->isLinked())
         return;
-    program->qOGLProgram()->bind();
+    program->bind();
 }
 
 /*!
diff --git a/src/context3d_p.h b/src/context3d_p.h
index 7c56758ba1a8d25820edf1e913360428d1841461..d69cc818173a2d4a1de840911b0746dbc32a6cf5 100644
--- a/src/context3d_p.h
+++ b/src/context3d_p.h
@@ -1153,7 +1153,7 @@ public:
 
     Q_INVOKABLE CanvasActiveInfo *getActiveAttrib(CanvasProgram *program, uint index);
     Q_INVOKABLE CanvasActiveInfo *getActiveUniform(CanvasProgram *program, uint index);
-
+    Q_INVOKABLE QVariantList getAttachedShaders(CanvasProgram *program);
 
     QString glEnumToString(glEnums value) const;
     float devicePixelRatio();
@@ -1164,9 +1164,9 @@ public:
     QRect glViewportRect() const;
     GLuint currentFramebuffer();
 
+
     /*
     TODO: Add these missing functions
-    sequence<WebGLShader>? getAttachedShaders(WebGLProgram program);
     any getFramebufferAttachmentParameter(GLenum target, GLenum attachment, GLenum pname);
     any getRenderbufferParameter(GLenum target, GLenum pname);
     any getTexParameter(GLenum target, GLenum pname);
diff --git a/src/program3d.cpp b/src/program3d.cpp
index eb5e3694aae62935802d7ae9aefc5c381afbc2ea..275a7c1e89a5acf13199b512b01c966c60ba21f4 100644
--- a/src/program3d.cpp
+++ b/src/program3d.cpp
@@ -75,6 +75,17 @@ int CanvasProgram::uniformLocation(const QString &name)
     return m_program->uniformLocation(name);
 }
 
+/*!
+ * \internal
+ */
+int CanvasProgram::attributeLocation(const QString &name)
+{
+    if (!m_program)
+        return -1;
+
+    return m_program->attributeLocation(name);
+}
+
 /*!
  * \internal
  */
@@ -86,9 +97,71 @@ bool CanvasProgram::isAlive()
 /*!
  * \internal
  */
-QOpenGLShaderProgram *CanvasProgram::qOGLProgram()
+void CanvasProgram::attach(CanvasShader *shader)
+{
+    if (m_attachedShaders.count(shader) == 0) {
+        m_attachedShaders.append(shader);
+        m_program->addShader(shader->qOGLShader());
+    }
+}
+
+/*!
+ * \internal
+ */
+void CanvasProgram::detach(CanvasShader *shader)
+{
+    if (m_attachedShaders.count(shader) > 0) {
+        m_attachedShaders.removeOne(shader);
+        m_program->removeShader(shader->qOGLShader());
+    }
+}
+
+/*!
+ * \internal
+ */
+const QList<CanvasShader *> &CanvasProgram::attachedShaders() const
+{
+    return m_attachedShaders;
+}
+
+/*!
+ * \internal
+ */
+void CanvasProgram::link()
+{
+    if (m_program)
+        m_program->link();
+}
+
+/*!
+ * \internal
+ */
+bool CanvasProgram::isLinked()
+{
+    if (!m_program)
+        return false;
+
+    return m_program->isLinked();
+}
+
+/*!
+ * \internal
+ */
+void CanvasProgram::bind()
+{
+    if (m_program)
+        m_program->bind();
+}
+
+/*!
+ * \internal
+ */
+void CanvasProgram::bindAttributeLocation(int index, const QString &name)
 {
-    return m_program;
+    if (!m_program)
+        return;
+
+    m_program->bindAttributeLocation(name, index);
 }
 
 /*!
@@ -98,6 +171,7 @@ void CanvasProgram::del()
 {
     delete m_program;
     m_program = 0;
+    m_attachedShaders.clear();
 }
 
 /*!
@@ -125,6 +199,17 @@ int CanvasProgram::id()
     return m_program->programId();
 }
 
+/*!
+ * \internal
+ */
+QString CanvasProgram::log()
+{
+    if (!m_program)
+        return "";
+
+    return m_program->log();
+}
+
 /*!
  * \internal
  */
diff --git a/src/program3d_p.h b/src/program3d_p.h
index e730df46a4e6bbebca0980a43f339004955124c5..23127ce6059b81de64d870913893ee8b2171b3be 100644
--- a/src/program3d_p.h
+++ b/src/program3d_p.h
@@ -49,10 +49,12 @@
 
 #include "context3d_p.h"
 #include "abstractobject3d_p.h"
+#include "shader3d_p.h"
 
 #include <QtGui/QOpenGLFunctions>
 #include <QtGui/QOpenGLShaderProgram>
 #include <QDebug>
+#include <QList>
 
 class CanvasProgram : public CanvasAbstractObject, protected QOpenGLFunctions
 {
@@ -64,16 +66,27 @@ public:
     virtual ~CanvasProgram();
 
     int uniformLocation(const QString &name);
+    int attributeLocation(const QString &name);
     void del();
     bool isAlive();
     void validateProgram();
-    QOpenGLShaderProgram *qOGLProgram();
     int id();
+    void link();
+    bool isLinked();
+    void bind();
+    void bindAttributeLocation(int index, const QString &name);
+
+    void attach(CanvasShader *shader);
+    void detach(CanvasShader *shader);
+    const QList<CanvasShader *> &attachedShaders() const;
+
+    QString log();
 
     friend QDebug operator<< (QDebug d, const CanvasProgram *program);
 
 private:
     QOpenGLShaderProgram *m_program;
+    QList<CanvasShader *> m_attachedShaders;
 
 signals:
     void idChanged(int id);